Merge "Activity start: Send featureId from context->AppOpsManager"
diff --git a/Android.bp b/Android.bp
index 12bc906..2dc8a4c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -263,8 +263,6 @@
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
         ":libupdate_engine_aidl",
-        // TODO: this needs to be removed when statsd-framework.jar is separated out
-        ":statsd_java_aidl",
         ":storaged_aidl",
         ":vold_aidl",
 
@@ -403,7 +401,6 @@
         "unsupportedappusage",
         "framework-media-stubs-systemapi",
         "framework-mediaprovider-stubs-systemapi",
-        "framework-tethering",
         "framework-telephony-stubs",
     ],
 
@@ -446,13 +443,6 @@
 }
 
 filegroup {
-    name: "graphicsstats_proto",
-    srcs: [
-        "libs/hwui/protos/graphicsstats.proto",
-    ],
-}
-
-filegroup {
     name: "libvibrator_aidl",
     srcs: [
         "core/java/android/os/IExternalVibrationController.aidl",
@@ -472,6 +462,7 @@
         "framework-permission-stubs-systemapi",
         "framework-wifi-stubs",
         "ike-stubs",
+        "framework-tethering-stubs",
     ],
     installable: true,
     javac_shard_size: 150,
@@ -496,6 +487,7 @@
         "//frameworks/base/apex/blobstore/framework",
         "//frameworks/base/apex/jobscheduler/framework",
         "//frameworks/base/apex/statsd/service",
+        "//frameworks/base/packages/Tethering/tests/unit",
     ],
 }
 
@@ -523,8 +515,7 @@
         "framework-statsd",
         "framework-wifi-stubs",
         "ike-stubs",
-        // TODO(b/147200698): should be the stub of framework-tethering
-        "framework-tethering",
+        "framework-tethering-stubs",
         // TODO (b/147688669) should be framework-telephony-stubs
         "framework-telephony",
         // TODO(jiyong): add stubs for APEXes here
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index a7b69c4..8e0ea98 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -102,7 +102,7 @@
 
                 durations.clear();
                 collectDigestDurationsFromTrace(parser, durations);
-                // get and delete blobId
+                // TODO: get and delete blobId before next iteration.
             }
         } finally {
             mAtraceUtils.stopTrace();
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
deleted file mode 100644
index 236f548..0000000
--- a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package android.os;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@LargeTest
-public class PackageManagerPerfTest {
-    private static final String PERMISSION_NAME_EXISTS =
-            "com.android.perftests.core.TestPermission";
-    private static final String PERMISSION_NAME_DOESNT_EXIST =
-            "com.android.perftests.core.TestBadPermission";
-    private static final ComponentName TEST_ACTIVITY =
-            new ComponentName("com.android.perftests.core",
-                    "android.perftests.utils.PerfTestActivity");
-
-    @Rule
-    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
-
-    @Test
-    public void testCheckPermissionExists() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-        final String packageName = TEST_ACTIVITY.getPackageName();
-
-        while (state.keepRunning()) {
-            int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
-        }
-    }
-
-    @Test
-    public void testCheckPermissionDoesntExist() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-        final String packageName = TEST_ACTIVITY.getPackageName();
-
-        while (state.keepRunning()) {
-            int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
-        }
-    }
-
-    @Test
-    public void testQueryIntentActivities() {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-        final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
-
-        while (state.keepRunning()) {
-            pm.queryIntentActivities(intent, 0);
-        }
-    }
-
-    @Test
-    public void testGetPackageInfo() throws Exception {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-        final String packageName = TEST_ACTIVITY.getPackageName();
-
-        while (state.keepRunning()) {
-            pm.getPackageInfo(packageName, 0);
-        }
-    }
-
-    @Test
-    public void testGetApplicationInfo() throws Exception {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-        final String packageName = TEST_ACTIVITY.getPackageName();
-        
-        while (state.keepRunning()) {
-            pm.getApplicationInfo(packageName, 0);
-        }
-    }
-
-    @Test
-    public void testGetActivityInfo() throws Exception {
-        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
-        
-        while (state.keepRunning()) {
-            pm.getActivityInfo(TEST_ACTIVITY, 0);
-        }
-    }
-}
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
new file mode 100644
index 0000000..17033e0
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -0,0 +1,21 @@
+android_test {
+    name: "PackageManagerPerfTests",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "platform-compat-test-rules",
+        "androidx.appcompat_appcompat",
+        "androidx.test.rules",
+        "androidx.test.ext.junit",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+    ],
+
+    libs: ["android.test.base"],
+
+    platform_apis: true,
+
+    test_suites: ["device-tests"],
+
+}
diff --git a/apct-tests/perftests/packagemanager/AndroidManifest.xml b/apct-tests/perftests/packagemanager/AndroidManifest.xml
new file mode 100644
index 0000000..520f4b5
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/AndroidManifest.xml
@@ -0,0 +1,89 @@
+<?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.perftests.packagemanager">
+
+    <permission android:name="com.android.perftests.packagemanager.TestPermission" />
+    <uses-permission android:name="com.android.perftests.packagemanager.TestPermission" />
+
+    <queries>
+        <package android:name="com.android.perftests.appenumeration0" />
+        <package android:name="com.android.perftests.appenumeration1" />
+        <package android:name="com.android.perftests.appenumeration2" />
+        <package android:name="com.android.perftests.appenumeration3" />
+        <package android:name="com.android.perftests.appenumeration4" />
+        <package android:name="com.android.perftests.appenumeration5" />
+        <package android:name="com.android.perftests.appenumeration6" />
+        <package android:name="com.android.perftests.appenumeration7" />
+        <package android:name="com.android.perftests.appenumeration8" />
+        <package android:name="com.android.perftests.appenumeration9" />
+        <package android:name="com.android.perftests.appenumeration10" />
+        <package android:name="com.android.perftests.appenumeration11" />
+        <package android:name="com.android.perftests.appenumeration12" />
+        <package android:name="com.android.perftests.appenumeration13" />
+        <package android:name="com.android.perftests.appenumeration14" />
+        <package android:name="com.android.perftests.appenumeration15" />
+        <package android:name="com.android.perftests.appenumeration16" />
+        <package android:name="com.android.perftests.appenumeration17" />
+        <package android:name="com.android.perftests.appenumeration18" />
+        <package android:name="com.android.perftests.appenumeration19" />
+        <package android:name="com.android.perftests.appenumeration20" />
+        <package android:name="com.android.perftests.appenumeration21" />
+        <package android:name="com.android.perftests.appenumeration22" />
+        <package android:name="com.android.perftests.appenumeration23" />
+        <package android:name="com.android.perftests.appenumeration24" />
+        <package android:name="com.android.perftests.appenumeration25" />
+        <package android:name="com.android.perftests.appenumeration26" />
+        <package android:name="com.android.perftests.appenumeration27" />
+        <package android:name="com.android.perftests.appenumeration28" />
+        <package android:name="com.android.perftests.appenumeration29" />
+        <package android:name="com.android.perftests.appenumeration30" />
+        <package android:name="com.android.perftests.appenumeration31" />
+        <package android:name="com.android.perftests.appenumeration32" />
+        <package android:name="com.android.perftests.appenumeration33" />
+        <package android:name="com.android.perftests.appenumeration34" />
+        <package android:name="com.android.perftests.appenumeration35" />
+        <package android:name="com.android.perftests.appenumeration36" />
+        <package android:name="com.android.perftests.appenumeration37" />
+        <package android:name="com.android.perftests.appenumeration38" />
+        <package android:name="com.android.perftests.appenumeration39" />
+        <package android:name="com.android.perftests.appenumeration40" />
+        <package android:name="com.android.perftests.appenumeration41" />
+        <package android:name="com.android.perftests.appenumeration42" />
+        <package android:name="com.android.perftests.appenumeration43" />
+        <package android:name="com.android.perftests.appenumeration44" />
+        <package android:name="com.android.perftests.appenumeration45" />
+        <package android:name="com.android.perftests.appenumeration46" />
+        <package android:name="com.android.perftests.appenumeration47" />
+        <package android:name="com.android.perftests.appenumeration48" />
+        <package android:name="com.android.perftests.appenumeration49" />
+    </queries>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.perftests.utils.PerfTestActivity">
+          <intent-filter>
+            <action android:name="com.android.perftests.packagemanager.PERFTEST" />
+          </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.perftests.packagemanager"/>
+
+</manifest>
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml
new file mode 100644
index 0000000..c112d87
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/AndroidTest.xml
@@ -0,0 +1,88 @@
+<?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.
+  -->
+<configuration description="Runs PackageManagerPerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="PackageManagerPerfTests.apk" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="force-queryable" value="false" />
+        <option name="test-file-name" value="QueriesAll0.apk" />
+        <option name="test-file-name" value="QueriesAll1.apk" />
+        <option name="test-file-name" value="QueriesAll2.apk" />
+        <option name="test-file-name" value="QueriesAll3.apk" />
+        <option name="test-file-name" value="QueriesAll4.apk" />
+        <option name="test-file-name" value="QueriesAll5.apk" />
+        <option name="test-file-name" value="QueriesAll6.apk" />
+        <option name="test-file-name" value="QueriesAll7.apk" />
+        <option name="test-file-name" value="QueriesAll8.apk" />
+        <option name="test-file-name" value="QueriesAll9.apk" />
+        <option name="test-file-name" value="QueriesAll10.apk" />
+        <option name="test-file-name" value="QueriesAll11.apk" />
+        <option name="test-file-name" value="QueriesAll12.apk" />
+        <option name="test-file-name" value="QueriesAll13.apk" />
+        <option name="test-file-name" value="QueriesAll14.apk" />
+        <option name="test-file-name" value="QueriesAll15.apk" />
+        <option name="test-file-name" value="QueriesAll16.apk" />
+        <option name="test-file-name" value="QueriesAll17.apk" />
+        <option name="test-file-name" value="QueriesAll18.apk" />
+        <option name="test-file-name" value="QueriesAll19.apk" />
+        <option name="test-file-name" value="QueriesAll20.apk" />
+        <option name="test-file-name" value="QueriesAll21.apk" />
+        <option name="test-file-name" value="QueriesAll22.apk" />
+        <option name="test-file-name" value="QueriesAll23.apk" />
+        <option name="test-file-name" value="QueriesAll24.apk" />
+        <option name="test-file-name" value="QueriesAll25.apk" />
+        <option name="test-file-name" value="QueriesAll26.apk" />
+        <option name="test-file-name" value="QueriesAll27.apk" />
+        <option name="test-file-name" value="QueriesAll28.apk" />
+        <option name="test-file-name" value="QueriesAll29.apk" />
+        <option name="test-file-name" value="QueriesAll30.apk" />
+        <option name="test-file-name" value="QueriesAll31.apk" />
+        <option name="test-file-name" value="QueriesAll32.apk" />
+        <option name="test-file-name" value="QueriesAll33.apk" />
+        <option name="test-file-name" value="QueriesAll34.apk" />
+        <option name="test-file-name" value="QueriesAll35.apk" />
+        <option name="test-file-name" value="QueriesAll36.apk" />
+        <option name="test-file-name" value="QueriesAll37.apk" />
+        <option name="test-file-name" value="QueriesAll38.apk" />
+        <option name="test-file-name" value="QueriesAll39.apk" />
+        <option name="test-file-name" value="QueriesAll40.apk" />
+        <option name="test-file-name" value="QueriesAll41.apk" />
+        <option name="test-file-name" value="QueriesAll42.apk" />
+        <option name="test-file-name" value="QueriesAll43.apk" />
+        <option name="test-file-name" value="QueriesAll44.apk" />
+        <option name="test-file-name" value="QueriesAll45.apk" />
+        <option name="test-file-name" value="QueriesAll46.apk" />
+        <option name="test-file-name" value="QueriesAll47.apk" />
+        <option name="test-file-name" value="QueriesAll48.apk" />
+        <option name="test-file-name" value="QueriesAll49.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.packagemanager" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/local/PackageManagerPerfTests" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
new file mode 100644
index 0000000..3cb1589
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
@@ -0,0 +1,314 @@
+// 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.
+
+android_test_helper_app {
+    name: "QueriesAll0",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration0",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll1",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration1",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll2",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration2",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll3",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration3",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll4",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration4",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll5",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration5",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll6",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration6",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll7",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration7",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll8",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration8",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll9",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration9",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll10",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration10",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll11",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration11",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll12",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration12",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll13",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration13",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll14",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration14",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll15",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration15",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll16",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration16",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll17",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration17",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll18",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration18",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll19",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration19",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll20",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration20",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll21",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration21",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll22",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration22",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll23",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration23",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll24",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration24",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll25",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration25",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll26",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration26",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll27",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration27",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll28",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration28",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll29",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration29",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll30",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration30",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll31",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration31",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll32",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration32",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll33",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration33",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll34",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration34",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll35",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration35",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll36",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration36",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll37",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration37",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll38",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration38",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll39",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration39",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll40",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration40",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll41",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration41",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll42",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration42",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll43",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration43",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll44",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration44",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll45",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration45",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll46",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration46",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll47",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration47",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll48",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration48",
+    ]
+}
+android_test_helper_app {
+    name: "QueriesAll49",
+    aaptflags: [
+        "--rename-manifest-package com.android.perftests.appenumeration49",
+    ]
+}
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml b/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
new file mode 100644
index 0000000..e2cfa04
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/apps/query-all/AndroidManifest.xml
@@ -0,0 +1,81 @@
+<?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.perftests.appenumeration">
+
+    <application android:hasCode="false" >
+        <activity android:name="android.perftests.utils.PerfTestActivity">
+            <intent-filter>
+                <action android:name="com.android.perftests.packagemanager.PERFTEST" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <queries>
+        <package android:name="com.android.perftests.appenumeration0" />
+        <package android:name="com.android.perftests.appenumeration1" />
+        <package android:name="com.android.perftests.appenumeration2" />
+        <package android:name="com.android.perftests.appenumeration3" />
+        <package android:name="com.android.perftests.appenumeration4" />
+        <package android:name="com.android.perftests.appenumeration5" />
+        <package android:name="com.android.perftests.appenumeration6" />
+        <package android:name="com.android.perftests.appenumeration7" />
+        <package android:name="com.android.perftests.appenumeration8" />
+        <package android:name="com.android.perftests.appenumeration9" />
+        <package android:name="com.android.perftests.appenumeration10" />
+        <package android:name="com.android.perftests.appenumeration11" />
+        <package android:name="com.android.perftests.appenumeration12" />
+        <package android:name="com.android.perftests.appenumeration13" />
+        <package android:name="com.android.perftests.appenumeration14" />
+        <package android:name="com.android.perftests.appenumeration15" />
+        <package android:name="com.android.perftests.appenumeration16" />
+        <package android:name="com.android.perftests.appenumeration17" />
+        <package android:name="com.android.perftests.appenumeration18" />
+        <package android:name="com.android.perftests.appenumeration19" />
+        <package android:name="com.android.perftests.appenumeration20" />
+        <package android:name="com.android.perftests.appenumeration21" />
+        <package android:name="com.android.perftests.appenumeration22" />
+        <package android:name="com.android.perftests.appenumeration23" />
+        <package android:name="com.android.perftests.appenumeration24" />
+        <package android:name="com.android.perftests.appenumeration25" />
+        <package android:name="com.android.perftests.appenumeration26" />
+        <package android:name="com.android.perftests.appenumeration27" />
+        <package android:name="com.android.perftests.appenumeration28" />
+        <package android:name="com.android.perftests.appenumeration29" />
+        <package android:name="com.android.perftests.appenumeration30" />
+        <package android:name="com.android.perftests.appenumeration31" />
+        <package android:name="com.android.perftests.appenumeration32" />
+        <package android:name="com.android.perftests.appenumeration33" />
+        <package android:name="com.android.perftests.appenumeration34" />
+        <package android:name="com.android.perftests.appenumeration35" />
+        <package android:name="com.android.perftests.appenumeration36" />
+        <package android:name="com.android.perftests.appenumeration37" />
+        <package android:name="com.android.perftests.appenumeration38" />
+        <package android:name="com.android.perftests.appenumeration39" />
+        <package android:name="com.android.perftests.appenumeration40" />
+        <package android:name="com.android.perftests.appenumeration41" />
+        <package android:name="com.android.perftests.appenumeration42" />
+        <package android:name="com.android.perftests.appenumeration43" />
+        <package android:name="com.android.perftests.appenumeration44" />
+        <package android:name="com.android.perftests.appenumeration45" />
+        <package android:name="com.android.perftests.appenumeration46" />
+        <package android:name="com.android.perftests.appenumeration47" />
+        <package android:name="com.android.perftests.appenumeration48" />
+        <package android:name="com.android.perftests.appenumeration49" />
+    </queries>
+
+</manifest>
\ No newline at end of file
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
new file mode 100644
index 0000000..d7428cf
--- /dev/null
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageManagerPerfTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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 libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class PackageManagerPerfTest {
+    private static final String PERMISSION_NAME_EXISTS =
+            "com.android.perftests.packagemanager.TestPermission";
+    private static final String PERMISSION_NAME_DOESNT_EXIST =
+            "com.android.perftests.packagemanager.TestBadPermission";
+    private static final String OTHER_PACKAGE_NAME = "com.android.perftests.appenumeration0";
+    private static final ComponentName TEST_ACTIVITY =
+            new ComponentName(OTHER_PACKAGE_NAME,
+                    "android.perftests.utils.PerfTestActivity");
+
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Rule
+    public final PlatformCompatChangeRule mPlatformCompatChangeRule =
+            new PlatformCompatChangeRule();
+
+    public PackageManagerPerfTest() throws PackageManager.NameNotFoundException {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionExists() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionExistsWithFiltering() {
+        testCheckPermissionExists();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionDoesntExist() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final String packageName = TEST_ACTIVITY.getPackageName();
+
+        while (state.keepRunning()) {
+            int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testCheckPermissionDoesntExistWithFiltering() {
+        testCheckPermissionDoesntExist();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testQueryIntentActivities() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
+
+        while (state.keepRunning()) {
+            pm.queryIntentActivities(intent, 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testQueryIntentActivitiesWithFiltering() {
+        testQueryIntentActivities();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetPackageInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getPackageInfo(OTHER_PACKAGE_NAME, 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetPackageInfoWithFiltering() throws Exception {
+        testGetPackageInfo();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetApplicationInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getApplicationInfo(OTHER_PACKAGE_NAME, 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetApplicationInfoWithFiltering() throws Exception {
+        testGetApplicationInfo();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetActivityInfo() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getActivityInfo(TEST_ACTIVITY, 0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetActivityInfoWithFiltering() throws Exception {
+        testGetActivityInfo();
+    }
+
+    @Test
+    @DisableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetInstalledPackages() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+
+        while (state.keepRunning()) {
+            pm.getInstalledPackages(0);
+        }
+    }
+
+    @Test
+    @EnableCompatChanges(PackageManager.FILTER_APPLICATION_QUERY)
+    public void testGetInstalledPackagesWithFiltering() throws Exception {
+        testGetInstalledPackages();
+    }
+}
diff --git a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
index bd3b673..f61ea85 100644
--- a/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
+++ b/apct-tests/perftests/textclassifier/src/android/view/textclassifier/TextClassificationManagerPerfTest.java
@@ -18,35 +18,60 @@
 import android.content.Context;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.SettingsHelper;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.LargeTest;
 
 import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Rule;
 import org.junit.Test;
 
 @LargeTest
 public class TextClassificationManagerPerfTest {
+    private static final String WRITE_DEVICE_CONFIG_PERMISSION =
+            "android.permission.WRITE_DEVICE_CONFIG";
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
+    private String mOriginalSystemTextclassifierStatus;
+
+    @BeforeClass
+    public static void setUpClass() {
+        InstrumentationRegistry.getInstrumentation().getUiAutomation()
+                .adoptShellPermissionIdentity(
+                        WRITE_DEVICE_CONFIG_PERMISSION);
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        InstrumentationRegistry
+                .getInstrumentation()
+                .getUiAutomation()
+                .dropShellPermissionIdentity();
+    }
+
+    @Before
+    public void setUp() {
+        // Saves config original value.
+        mOriginalSystemTextclassifierStatus = DeviceConfig.getProperty(
+                DeviceConfig.NAMESPACE_TEXTCLASSIFIER, "system_textclassifier_enabled");
+    }
+
     @After
     public void tearDown() {
-        SettingsHelper.delete(
-                SettingsHelper.NAMESPACE_GLOBAL, Settings.Global.TEXT_CLASSIFIER_CONSTANTS);
+        // Restores config original value.
+        enableSystemTextclassifier(mOriginalSystemTextclassifierStatus);
     }
 
     @Test
     public void testGetTextClassifier_systemTextClassifierDisabled() {
         Context context = InstrumentationRegistry.getTargetContext();
-        SettingsHelper.set(
-                SettingsHelper.NAMESPACE_GLOBAL,
-                Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
-                "system_textclassifier_enabled=false");
+        enableSystemTextclassifier(String.valueOf(false));
         TextClassificationManager textClassificationManager =
                 context.getSystemService(TextClassificationManager.class);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -59,10 +84,7 @@
     @Test
     public void testGetTextClassifier_systemTextClassifierEnabled() {
         Context context = InstrumentationRegistry.getTargetContext();
-        SettingsHelper.set(
-                SettingsHelper.NAMESPACE_GLOBAL,
-                Settings.Global.TEXT_CLASSIFIER_CONSTANTS,
-                "system_textclassifier_enabled=true");
+        enableSystemTextclassifier(String.valueOf(true));
         TextClassificationManager textClassificationManager =
                 context.getSystemService(TextClassificationManager.class);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -71,4 +93,9 @@
             textClassificationManager.invalidateForTesting();
         }
     }
+
+    private void enableSystemTextclassifier(String enabled) {
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                "system_textclassifier_enabled", enabled, /* makeDefault */ false);
+    }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 69f4748..939164e 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -494,9 +494,10 @@
         private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
         private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
 
-        private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
+        private static final String DEPRECATED_KEY_MAX_STANDARD_RESCHEDULE_COUNT
                 = "max_standard_reschedule_count";
-        private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
+        private static final String DEPRECATED_KEY_MAX_WORK_RESCHEDULE_COUNT =
+                "max_work_reschedule_count";
         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
         private static final String DEPRECATED_KEY_STANDBY_HEARTBEAT_TIME =
@@ -525,8 +526,6 @@
         private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
-        private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
-        private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
@@ -640,16 +639,6 @@
                         "screen_off_job_concurrency_increase_delay_ms", 30_000);
 
         /**
-         * The maximum number of times we allow a job to have itself rescheduled before
-         * giving up on it, for standard jobs.
-         */
-        int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
-        /**
-         * The maximum number of times we allow a job to have itself rescheduled before
-         * giving up on it, for jobs that are executing work.
-         */
-        int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
-        /**
          * The minimum backoff time to allow for linear backoff.
          */
         long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
@@ -735,10 +724,6 @@
 
             SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser);
 
-            MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
-                    DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
-            MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
-                    DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
             MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
                     DEFAULT_MIN_LINEAR_BACKOFF_TIME);
             MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
@@ -790,8 +775,6 @@
 
             SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
 
-            pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
-            pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
             pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
             pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
@@ -827,8 +810,6 @@
             SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
                     ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
 
-            proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
-            proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
             proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
             proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
@@ -1390,18 +1371,8 @@
                     // Effective standby bucket can change after this in some situations so use
                     // the real bucket so that the job is tracked by the controllers.
                     if (js.getStandbyBucket() == RESTRICTED_INDEX) {
-                        js.addDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
-                        js.addDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
-                        js.addDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
-                        js.addDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
-
                         mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
                     } else {
-                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
-                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
-                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
-                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_IDLE);
-
                         mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
                     }
                 }
@@ -1738,19 +1709,6 @@
         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
         long delayMillis;
 
-        if (failureToReschedule.hasWorkLocked()) {
-            if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
-                Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
-                        + backoffAttempts + " > work limit "
-                        + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
-                return null;
-            }
-        } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
-            Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
-                    + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
-            return null;
-        }
-
         switch (job.getBackoffPolicy()) {
             case JobInfo.BACKOFF_POLICY_LINEAR: {
                 long backoff = initialBackoffMillis;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index f706260..1e89158 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -17,6 +17,8 @@
 package com.android.server.job.controllers;
 
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import android.app.AppGlobals;
@@ -63,26 +65,36 @@
  * @hide
  */
 public final class JobStatus {
-    static final String TAG = "JobSchedulerService";
+    private static final String TAG = "JobScheduler.JobStatus";
     static final boolean DEBUG = JobSchedulerService.DEBUG;
 
     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
     public static final long NO_EARLIEST_RUNTIME = 0L;
 
-    public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
-    public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
-    public static final int CONSTRAINT_BATTERY_NOT_LOW =
-            JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
+    static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
+    static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
+    static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
     static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
     static final int CONSTRAINT_DEADLINE = 1<<30;
-    public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
+    static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
 
     /**
+     * The additional set of dynamic constraints that must be met if the job's effective bucket is
+     * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
+     * need network.
+     */
+    private static final int DYNAMIC_RESTRICTED_CONSTRAINTS =
+            CONSTRAINT_BATTERY_NOT_LOW
+                    | CONSTRAINT_CHARGING
+                    | CONSTRAINT_CONNECTIVITY
+                    | CONSTRAINT_IDLE;
+
+    /**
      * The constraints that we want to log to statsd.
      *
      * Constraints that can be inferred from other atoms have been excluded to avoid logging too
@@ -419,7 +431,11 @@
         this.requiredConstraints = requiredConstraints;
         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
         mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
-        mReadyDynamicSatisfied = true;
+        if (standbyBucket == RESTRICTED_INDEX) {
+            addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
+        } else {
+            mReadyDynamicSatisfied = true;
+        }
 
         mLastSuccessfulRunTime = lastSuccessfulRunTime;
         mLastFailedRunTime = lastFailedRunTime;
@@ -727,6 +743,14 @@
     }
 
     public void setStandbyBucket(int newBucket) {
+        if (newBucket == RESTRICTED_INDEX) {
+            // Adding to the bucket.
+            addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
+        } else if (standbyBucket == RESTRICTED_INDEX) {
+            // Removing from the RESTRICTED bucket.
+            removeDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
+        }
+
         standbyBucket = newBucket;
     }
 
@@ -1054,6 +1078,11 @@
         if (old == state) {
             return false;
         }
+        if (DEBUG) {
+            Slog.v(TAG,
+                    "Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for "
+                            + toShortString());
+        }
         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
         mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
         mReadyDynamicSatisfied =
@@ -1086,38 +1115,40 @@
     }
 
     /**
-     * Indicates that this job cannot run without the specified constraint. This is evaluated
+     * Indicates that this job cannot run without the specified constraints. This is evaluated
      * separately from the job's explicitly requested constraints and MUST be satisfied before
      * the job can run if the app doesn't have quota.
-     *
      */
-    public void addDynamicConstraint(int constraint) {
-        if (constraint == CONSTRAINT_WITHIN_QUOTA) {
+    private void addDynamicConstraints(int constraints) {
+        if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
+            // Quota should never be used as a dynamic constraint.
             Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
-            return;
+            constraints &= ~CONSTRAINT_WITHIN_QUOTA;
         }
 
         // Connectivity and content trigger are special since they're only valid to add if the
         // job has requested network or specific content URIs. Adding these constraints to jobs
         // that don't need them doesn't make sense.
-        if ((constraint == CONSTRAINT_CONNECTIVITY && !hasConnectivityConstraint())
-                || (constraint == CONSTRAINT_CONTENT_TRIGGER && !hasContentTriggerConstraint())) {
-            return;
+        if (!hasConnectivityConstraint()) {
+            constraints &= ~CONSTRAINT_CONNECTIVITY;
+        }
+        if (!hasContentTriggerConstraint()) {
+            constraints &= ~CONSTRAINT_CONTENT_TRIGGER;
         }
 
-        mDynamicConstraints |= constraint;
+        mDynamicConstraints |= constraints;
         mReadyDynamicSatisfied =
                 mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
     }
 
     /**
-     * Removes a dynamic constraint from a job, meaning that the requirement is not required for
+     * Removes dynamic constraints from a job, meaning that the requirements are not required for
      * the job to run (if the job itself hasn't requested the constraint. This is separate from
      * the job's explicitly requested constraints and does not remove those requested constraints.
      *
      */
-    public void removeDynamicConstraint(int constraint) {
-        mDynamicConstraints &= ~constraint;
+    private void removeDynamicConstraints(int constraints) {
+        mDynamicConstraints &= ~constraints;
         mReadyDynamicSatisfied =
                 mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
     }
@@ -1193,7 +1224,11 @@
 
     private boolean isReady(int satisfiedConstraints) {
         // Quota and dynamic constraints trump all other constraints.
-        if (!mReadyWithinQuota && !mReadyDynamicSatisfied) {
+        // NEVER jobs are not supposed to run at all. Since we're using quota to allow parole
+        // sessions (exempt from dynamic restrictions), we need the additional check to ensure
+        // that NEVER jobs don't run.
+        // TODO: cleanup quota and standby bucket management so we don't need the additional checks
+        if ((!mReadyWithinQuota && !mReadyDynamicSatisfied) || standbyBucket == NEVER_INDEX) {
             return false;
         }
         // Deadline constraint trumps other constraints besides quota and dynamic (except for
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 91df098..23ae8af 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -38,6 +38,12 @@
         "android.media",
     ],
 
+    optimize: {
+        enabled: true,
+        shrink: true,
+        proguard_flags_files: ["updatable-media-proguard.flags"],
+    },
+
     installable: true,
 
     // TODO: build against stable API surface. Use core_platform for now to avoid
diff --git a/apex/media/framework/updatable-media-proguard.flags b/apex/media/framework/updatable-media-proguard.flags
new file mode 100644
index 0000000..4e7d842
--- /dev/null
+++ b/apex/media/framework/updatable-media-proguard.flags
@@ -0,0 +1,2 @@
+# Keep all symbols in android.media.
+-keep class android.media.* {*;}
diff --git a/apex/permission/testing/Android.bp b/apex/permission/testing/Android.bp
index f8978dc..63bf0a0 100644
--- a/apex/permission/testing/Android.bp
+++ b/apex/permission/testing/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-apex {
+apex_test {
     name: "test_com.android.permission",
     visibility: [
         "//system/apex/tests",
diff --git a/apex/sdkextensions/derive_sdk/derive_sdk.cpp b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
index 6fb7ef4..900193a 100644
--- a/apex/sdkextensions/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkextensions/derive_sdk/derive_sdk.cpp
@@ -69,7 +69,7 @@
     auto itr = std::min_element(versions.begin(), versions.end());
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
-    if (!android::base::SetProperty("ro.build.version.extensions.r", prop_value)) {
+    if (!android::base::SetProperty("build.version.extensions.r", prop_value)) {
         LOG(ERROR) << "failed to set sdk_info prop";
         return EXIT_FAILURE;
     }
diff --git a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
index a8a7eff..103b53e 100644
--- a/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkextensions/framework/java/android/os/ext/SdkExtensions.java
@@ -38,7 +38,7 @@
 
     private static final int R_EXTENSION_INT;
     static {
-        R_EXTENSION_INT = SystemProperties.getInt("ro.build.version.extensions.r", 0);
+        R_EXTENSION_INT = SystemProperties.getInt("build.version.extensions.r", 0);
     }
 
     /**
diff --git a/apex/sdkextensions/framework/java/android/os/ext/test/Test.java b/apex/sdkextensions/framework/java/android/os/ext/test/Test.java
new file mode 100644
index 0000000..1715f49
--- /dev/null
+++ b/apex/sdkextensions/framework/java/android/os/ext/test/Test.java
@@ -0,0 +1,53 @@
+/*
+ * 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.ext.test;
+
+import android.annotation.SystemApi;
+
+/**
+ * This class exists temporarily to verify SDK updates are working properly.
+ * @deprecated Do not use.
+ */
+@Deprecated
+public class Test {
+
+    public Test() { }
+
+    /** @hide */
+    public void testA() {}
+
+    /** @hide */
+    public void testB() {}
+
+    /** @hide */
+    public void testC() {}
+
+    /** @hide */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public void testD() {}
+
+    public void testE() {}
+
+    /** @hide */
+    @SystemApi
+    public void testF() {}
+
+    /** @hide */
+    @SystemApi
+    public void testG() {}
+
+}
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 6d639fd..db5f439 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -13,35 +13,31 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 //
-
-// TODO(b/145815909): move StatsDimensionsValue.aidl here
 filegroup {
-    name: "statsd_aidl",
+    name: "statsd_java_aidl",
+    srcs: ["**/*.aidl"],
+}
+
+aidl_interface {
+    name: "statsd-aidl",
     srcs: [
         "android/os/IPendingIntentRef.aidl",
         "android/os/IPullAtomCallback.aidl",
         "android/os/IPullAtomResultReceiver.aidl",
         "android/os/IStatsCompanionService.aidl",
         "android/os/IStatsd.aidl",
+        "android/os/StatsDimensionsValueParcel.aidl",
         "android/util/StatsEventParcel.aidl",
     ],
-}
-
-filegroup {
-    name: "statsd_java_aidl",
-    srcs: ["**/*.aidl"],
-}
-
-// This library is currently unused
-aidl_interface {
-    name: "stats-event-parcel-aidl",
-    srcs: ["android/util/StatsEventParcel.aidl"],
     backend: {
         java: {
-            sdk_version: "28",
+            enabled: false, // the platform uses statsd_java_aidl
         },
         cpp: {
-            enabled: false,
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
         }
     }
 }
diff --git a/apex/statsd/aidl/android/os/IPendingIntentRef.aidl b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
index 6b9e467..000a699 100644
--- a/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
+++ b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
@@ -16,7 +16,7 @@
 
 package android.os;
 
-import android.os.StatsDimensionsValue;
+import android.os.StatsDimensionsValueParcel;
 
 /**
   * Binder interface to hold a PendingIntent for StatsCompanionService.
@@ -42,5 +42,5 @@
       */
      oneway void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
                                          long subscriptionRuleId, in String[] cookies,
-                                         in StatsDimensionsValue dimensionsValue);
-}
\ No newline at end of file
+                                         in StatsDimensionsValueParcel dimensionsValueParcel);
+}
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index 253b2c1..10b1e5b 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -222,12 +222,6 @@
     const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04;
 
     /**
-     * Logs an event for watchdog rollbacks.
-     */
-     oneway void sendWatchdogRollbackOccurredAtom(in int rollbackType, in String packageName,
-         in long packageVersionCode, in int rollbackReason, in String failingPackageName);
-
-    /**
      * Returns the most recently registered experiment IDs.
      */
     long[] getRegisteredExperimentIds();
diff --git a/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl b/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl
new file mode 100644
index 0000000..a8685e3
--- /dev/null
+++ b/apex/statsd/aidl/android/os/StatsDimensionsValueParcel.aidl
@@ -0,0 +1,21 @@
+package android.os;
+
+/**
+ * @hide
+ */
+parcelable StatsDimensionsValueParcel {
+    /**
+     * Field equals:
+     *      - atomTag for top level StatsDimensionsValueParcel
+     *      - position in dimension for all other levels
+     */
+    int field;
+    int valueType;
+
+    String stringValue;
+    int intValue;
+    long longValue;
+    boolean boolValue;
+    float floatValue;
+    StatsDimensionsValueParcel[] tupleValue;
+}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index d85ae69..e3c70d7 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -15,9 +15,9 @@
 filegroup {
     name: "framework-statsd-sources",
     srcs: [
-    "java/**/*.java",
+        "java/**/*.java",
+        ":statsd_java_aidl",
     ],
-    path: "java",
 }
 
 java_library {
diff --git a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
index 886130f..71d4359 100644
--- a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
+++ b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
@@ -96,6 +96,47 @@
     }
 
     /**
+     * Creates a {@code StatsDimensionsValue} from a StatsDimensionsValueParcel
+     * TODO(b/149103391): Make StatsDimensionsValue a wrapper on top of
+     * StatsDimensionsValueParcel.
+     *
+     * @hide
+     */
+    public StatsDimensionsValue(StatsDimensionsValueParcel parcel) {
+        mField = parcel.field;
+        mValueType = parcel.valueType;
+        switch (mValueType) {
+            case STRING_VALUE_TYPE:
+                mValue = parcel.stringValue;
+                break;
+            case INT_VALUE_TYPE:
+                mValue = parcel.intValue;
+                break;
+            case LONG_VALUE_TYPE:
+                mValue = parcel.longValue;
+                break;
+            case BOOLEAN_VALUE_TYPE:
+                mValue = parcel.boolValue;
+                break;
+            case FLOAT_VALUE_TYPE:
+                mValue = parcel.floatValue;
+                break;
+            case TUPLE_VALUE_TYPE:
+                StatsDimensionsValue[] values = new StatsDimensionsValue[parcel.tupleValue.length];
+                for (int i = 0; i < parcel.tupleValue.length; i++) {
+                    values[i] = new StatsDimensionsValue(parcel.tupleValue[i]);
+                }
+                mValue = values;
+                break;
+            default:
+                Slog.w(TAG, "StatsDimensionsValueParcel contains bad valueType: " + mValueType);
+                mValue = null;
+                break;
+        }
+    }
+
+
+    /**
      * Return the field, i.e. the tag of a statsd atom.
      *
      * @return the field
diff --git a/apex/statsd/framework/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java
index 7910737..e7659d8 100644
--- a/apex/statsd/framework/java/android/util/StatsLog.java
+++ b/apex/statsd/framework/java/android/util/StatsLog.java
@@ -171,52 +171,11 @@
                 state,
                 proto.getBytes(),
                 0,
-                0);
+                0,
+                false);
         return true;
     }
 
-    /**
-     * Logs an event for watchdog rollbacks.
-     *
-     * @param rollbackType          state of the rollback.
-     * @param packageName           package name being rolled back.
-     * @param packageVersionCode    version of the package being rolled back.
-     * @param rollbackReason        reason the package is being rolled back.
-     * @param failingPackageName    the package name causing the failure.
-     *
-     * @return True if the log request was sent to statsd.
-     *
-     * @hide
-     */
-    @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
-    public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName,
-            long packageVersionCode, int rollbackReason, String failingPackageName) {
-        synchronized (sLogLock) {
-            try {
-                IStatsd service = getIStatsdLocked();
-                if (service == null) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Failed to find statsd when logging event");
-                    }
-                    return false;
-                }
-
-                service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName,
-                        packageVersionCode, rollbackReason, failingPackageName);
-                return true;
-            } catch (RemoteException e) {
-                sService = null;
-                if (DEBUG) {
-                    Slog.d(TAG,
-                            "Failed to connect to StatsCompanionService when logging "
-                                    + "WatchdogRollbackOccurred");
-                }
-                return false;
-            }
-        }
-    }
-
-
     private static IStatsd getIStatsdLocked() throws RemoteException {
         if (sService != null) {
             return sService;
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
index 4495dc9..c1ba73f 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
@@ -24,7 +24,8 @@
 import android.os.IPendingIntentRef;
 import android.os.Process;
 import android.os.StatsDimensionsValue;
-import android.util.Slog;
+import android.os.StatsDimensionsValueParcel;
+import android.util.Log;
 
 import com.android.server.SystemService;
 
@@ -40,6 +41,9 @@
 
     private static final int AID_STATSD = 1066;
 
+    private static final String STATS_COMPANION_SERVICE = "statscompanion";
+    private static final String STATS_MANAGER_SERVICE = "statsmanager";
+
     static void enforceStatsdCallingUid() {
         if (Binder.getCallingPid() == Process.myPid()) {
             return;
@@ -68,14 +72,12 @@
             mStatsManagerService.setStatsCompanionService(mStatsCompanionService);
 
             try {
-                publishBinderService(Context.STATS_COMPANION_SERVICE,
-                        mStatsCompanionService);
-                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
-                publishBinderService(Context.STATS_MANAGER_SERVICE,
-                        mStatsManagerService);
-                if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_MANAGER_SERVICE);
+                publishBinderService(STATS_COMPANION_SERVICE, mStatsCompanionService);
+                if (DEBUG) Log.d(TAG, "Published " + STATS_COMPANION_SERVICE);
+                publishBinderService(STATS_MANAGER_SERVICE, mStatsManagerService);
+                if (DEBUG) Log.d(TAG, "Published " + STATS_MANAGER_SERVICE);
             } catch (Exception e) {
-                Slog.e(TAG, "Failed to publishBinderService", e);
+                Log.e(TAG, "Failed to publishBinderService", e);
             }
         }
 
@@ -124,7 +126,7 @@
             try {
                 mPendingIntent.send(mContext, CODE_DATA_BROADCAST, intent, null, null);
             } catch (PendingIntent.CanceledException e) {
-                Slog.w(TAG, "Unable to send PendingIntent");
+                Log.w(TAG, "Unable to send PendingIntent");
             }
         }
 
@@ -136,17 +138,19 @@
             try {
                 mPendingIntent.send(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
                 if (DEBUG) {
-                    Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
+                    Log.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
                 }
             } catch (PendingIntent.CanceledException e) {
-                Slog.w(TAG, "Unable to send active configs changed broadcast using PendingIntent");
+                Log.w(TAG, "Unable to send active configs changed broadcast using PendingIntent");
             }
         }
 
         @Override
         public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
-                long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) {
+                long subscriptionRuleId, String[] cookies,
+                StatsDimensionsValueParcel dimensionsValueParcel) {
             enforceStatsdCallingUid();
+            StatsDimensionsValue dimensionsValue = new StatsDimensionsValue(dimensionsValueParcel);
             Intent intent =
                     new Intent()
                             .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
@@ -162,7 +166,7 @@
                     StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
 
             if (DEBUG) {
-                Slog.d(TAG,
+                Log.d(TAG,
                         String.format(
                                 "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
                                 configUid, configId, subscriptionId, subscriptionRuleId,
@@ -172,7 +176,7 @@
             try {
                 mPendingIntent.send(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
             } catch (PendingIntent.CanceledException e) {
-                Slog.w(TAG,
+                Log.w(TAG,
                         "Unable to send using PendingIntent from uid " + configUid
                                 + "; presumably it had been cancelled.");
             }
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index a735cb8..cb167c3 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -40,14 +40,10 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.Slog;
+import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.os.LooperStats;
-import com.android.internal.util.DumpUtils;
-import com.android.server.BinderCallsStatsService;
-import com.android.server.LocalServices;
 
 import libcore.io.IoUtils;
 
@@ -89,6 +85,12 @@
 
     public static final int DEATH_THRESHOLD = 10;
 
+    // TODO(b/149090705): Implement an alternative to sending broadcast with @hide flag
+    // FLAG_RECEIVER_INCLUDE_BACKGROUND. Instead of using the flag, find the
+    // list of registered broadcast receivers and send them directed broadcasts
+    // to wake them up. See b/147374337.
+    private static final int FLAG_RECEIVER_INCLUDE_BACKGROUND = 0x01000000;
+
     static final class CompanionHandler extends Handler {
         CompanionHandler(Looper looper) {
             super(looper);
@@ -126,7 +128,7 @@
             public void onReceive(Context context, Intent intent) {
                 synchronized (sStatsdLock) {
                     if (sStatsd == null) {
-                        Slog.w(TAG, "Could not access statsd for UserUpdateReceiver");
+                        Log.w(TAG, "Could not access statsd for UserUpdateReceiver");
                         return;
                     }
                     try {
@@ -134,14 +136,14 @@
                         // Needed since the new user basically has a version of every app.
                         informAllUidsLocked(context);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
+                        Log.e(TAG, "Failed to inform statsd latest update of all apps", e);
                         forgetEverythingLocked();
                     }
                 }
             }
         };
         mShutdownEventReceiver = new ShutdownEventReceiver();
-        if (DEBUG) Slog.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
+        if (DEBUG) Log.d(TAG, "Registered receiver for ACTION_PACKAGE_REPLACED and ADDED.");
         HandlerThread handlerThread = new HandlerThread(TAG);
         handlerThread.start();
         mHandler = new CompanionHandler(handlerThread.getLooper());
@@ -171,21 +173,21 @@
         PackageManager pm = context.getPackageManager();
         final List<UserHandle> users = um.getUserHandles(true);
         if (DEBUG) {
-            Slog.d(TAG, "Iterating over " + users.size() + " userHandles.");
+            Log.d(TAG, "Iterating over " + users.size() + " userHandles.");
         }
 
         ParcelFileDescriptor[] fds;
         try {
             fds = ParcelFileDescriptor.createPipe();
         } catch (IOException e) {
-            Slog.e(TAG, "Failed to create a pipe to send uid map data.", e);
+            Log.e(TAG, "Failed to create a pipe to send uid map data.", e);
             return;
         }
         sStatsd.informAllUidData(fds[0]);
         try {
             fds[0].close();
         } catch (IOException e) {
-            Slog.e(TAG, "Failed to close the read side of the pipe.", e);
+            Log.e(TAG, "Failed to close the read side of the pipe.", e);
         }
         final ParcelFileDescriptor writeFd = fds[1];
         HandlerThread backgroundThread = new HandlerThread(
@@ -239,7 +241,7 @@
                 }
                 output.flush();
                 if (DEBUG) {
-                    Slog.d(TAG, "Sent data for " + numRecords + " apps");
+                    Log.d(TAG, "Sent data for " + numRecords + " apps");
                 }
             } finally {
                 IoUtils.closeQuietly(fout);
@@ -261,10 +263,10 @@
                     && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                 return; // Keep only replacing or normal add and remove.
             }
-            if (DEBUG) Slog.d(TAG, "StatsCompanionService noticed an app was updated.");
+            if (DEBUG) Log.d(TAG, "StatsCompanionService noticed an app was updated.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of an app update");
+                    Log.w(TAG, "Could not access statsd to inform it of an app update");
                     return;
                 }
                 try {
@@ -299,7 +301,7 @@
                                 installer == null ? "" : installer);
                     }
                 } catch (Exception e) {
-                    Slog.w(TAG, "Failed to inform statsd of an app update", e);
+                    Log.w(TAG, "Failed to inform statsd of an app update", e);
                 }
             }
         }
@@ -308,18 +310,18 @@
     public final static class AnomalyAlarmListener implements OnAlarmListener {
         @Override
         public void onAlarm() {
-            Slog.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
+            Log.i(TAG, "StatsCompanionService believes an anomaly has occurred at time "
                     + System.currentTimeMillis() + "ms.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+                    Log.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
                     return;
                 }
                 try {
                     // Two-way call to statsd to retain AlarmManager wakelock
                     sStatsd.informAnomalyAlarmFired();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
+                    Log.w(TAG, "Failed to inform statsd of anomaly alarm firing", e);
                 }
             }
             // AlarmManager releases its own wakelock here.
@@ -330,18 +332,18 @@
         @Override
         public void onAlarm() {
             if (DEBUG) {
-                Slog.d(TAG, "Time to poll something.");
+                Log.d(TAG, "Time to poll something.");
             }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
+                    Log.w(TAG, "Could not access statsd to inform it of pulling alarm firing.");
                     return;
                 }
                 try {
                     // Two-way call to statsd to retain AlarmManager wakelock
                     sStatsd.informPollAlarmFired();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
+                    Log.w(TAG, "Failed to inform statsd of pulling alarm firing.", e);
                 }
             }
         }
@@ -351,18 +353,18 @@
         @Override
         public void onAlarm() {
             if (DEBUG) {
-                Slog.d(TAG, "Time to trigger periodic alarm.");
+                Log.d(TAG, "Time to trigger periodic alarm.");
             }
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
+                    Log.w(TAG, "Could not access statsd to inform it of periodic alarm firing.");
                     return;
                 }
                 try {
                     // Two-way call to statsd to retain AlarmManager wakelock
                     sStatsd.informAlarmForSubscriberTriggeringFired();
                 } catch (RemoteException e) {
-                    Slog.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
+                    Log.w(TAG, "Failed to inform statsd of periodic alarm firing.", e);
                 }
             }
             // AlarmManager releases its own wakelock here.
@@ -381,16 +383,16 @@
                 return;
             }
 
-            Slog.i(TAG, "StatsCompanionService noticed a shutdown.");
+            Log.i(TAG, "StatsCompanionService noticed a shutdown.");
             synchronized (sStatsdLock) {
                 if (sStatsd == null) {
-                    Slog.w(TAG, "Could not access statsd to inform it of a shutdown event.");
+                    Log.w(TAG, "Could not access statsd to inform it of a shutdown event.");
                     return;
                 }
                 try {
                     sStatsd.informDeviceShutdown();
                 } catch (Exception e) {
-                    Slog.w(TAG, "Failed to inform statsd of a shutdown event.", e);
+                    Log.w(TAG, "Failed to inform statsd of a shutdown event.", e);
                 }
             }
         }
@@ -399,7 +401,7 @@
     @Override // Binder call
     public void setAnomalyAlarm(long timestampMs) {
         StatsCompanion.enforceStatsdCallingUid();
-        if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
+        if (DEBUG) Log.d(TAG, "Setting anomaly alarm for " + timestampMs);
         final long callingToken = Binder.clearCallingIdentity();
         try {
             // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
@@ -415,7 +417,7 @@
     @Override // Binder call
     public void cancelAnomalyAlarm() {
         StatsCompanion.enforceStatsdCallingUid();
-        if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
+        if (DEBUG) Log.d(TAG, "Cancelling anomaly alarm");
         final long callingToken = Binder.clearCallingIdentity();
         try {
             mAlarmManager.cancel(mAnomalyAlarmListener);
@@ -428,7 +430,7 @@
     public void setAlarmForSubscriberTriggering(long timestampMs) {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG,
+            Log.d(TAG,
                     "Setting periodic alarm in about " + (timestampMs
                             - SystemClock.elapsedRealtime()));
         }
@@ -447,7 +449,7 @@
     public void cancelAlarmForSubscriberTriggering() {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "Cancelling periodic alarm");
+            Log.d(TAG, "Cancelling periodic alarm");
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
@@ -461,7 +463,7 @@
     public void setPullingAlarm(long nextPullTimeMs) {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "Setting pulling alarm in about "
+            Log.d(TAG, "Setting pulling alarm in about "
                     + (nextPullTimeMs - SystemClock.elapsedRealtime()));
         }
         final long callingToken = Binder.clearCallingIdentity();
@@ -479,7 +481,7 @@
     public void cancelPullingAlarm() {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "Cancelling pulling alarm");
+            Log.d(TAG, "Cancelling pulling alarm");
         }
         final long callingToken = Binder.clearCallingIdentity();
         try {
@@ -493,11 +495,11 @@
     public void statsdReady() {
         StatsCompanion.enforceStatsdCallingUid();
         if (DEBUG) {
-            Slog.d(TAG, "learned that statsdReady");
+            Log.d(TAG, "learned that statsdReady");
         }
         sayHiToStatsd(); // tell statsd that we're ready too and link to it
         mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
-                        .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
+                        .addFlags(FLAG_RECEIVER_INCLUDE_BACKGROUND),
                 UserHandle.SYSTEM, android.Manifest.permission.DUMP);
     }
 
@@ -509,7 +511,7 @@
             try {
                 informAllUidsLocked(mContext);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to trigger uid snapshot.", e);
+                Log.e(TAG, "Failed to trigger uid snapshot.", e);
             } finally {
                 restoreCallingIdentity(token);
             }
@@ -540,7 +542,7 @@
      * Now that the android system is ready, StatsCompanion is ready too, so inform statsd.
      */
     void systemReady() {
-        if (DEBUG) Slog.d(TAG, "Learned that systemReady");
+        if (DEBUG) Log.d(TAG, "Learned that systemReady");
         sayHiToStatsd();
     }
 
@@ -555,27 +557,27 @@
     private void sayHiToStatsd() {
         synchronized (sStatsdLock) {
             if (sStatsd != null) {
-                Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
+                Log.e(TAG, "Trying to fetch statsd, but it was already fetched",
                         new IllegalStateException(
                                 "sStatsd is not null when being fetched"));
                 return;
             }
             sStatsd = fetchStatsdService();
             if (sStatsd == null) {
-                Slog.i(TAG,
+                Log.i(TAG,
                         "Could not yet find statsd to tell it that StatsCompanion is "
                                 + "alive.");
                 return;
             }
             mStatsManagerService.statsdReady(sStatsd);
-            if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
+            if (DEBUG) Log.d(TAG, "Saying hi to statsd");
             try {
                 sStatsd.statsCompanionReady();
                 // If the statsCompanionReady two-way binder call returns, link to statsd.
                 try {
                     sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
                 } catch (RemoteException e) {
-                    Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+                    Log.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
                     forgetEverythingLocked();
                 }
                 // Setup broadcast receiver for updates.
@@ -605,9 +607,9 @@
                 } finally {
                     restoreCallingIdentity(token);
                 }
-                Slog.i(TAG, "Told statsd that StatsCompanionService is alive.");
+                Log.i(TAG, "Told statsd that StatsCompanionService is alive.");
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
+                Log.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
                 forgetEverythingLocked();
             }
         }
@@ -616,7 +618,7 @@
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
-            Slog.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
+            Log.i(TAG, "Statsd is dead - erase all my knowledge, except pullers");
             synchronized (sStatsdLock) {
                 long now = SystemClock.elapsedRealtime();
                 for (Long timeMillis : mDeathTimeMillis) {
@@ -656,22 +658,15 @@
         cancelAnomalyAlarm();
         cancelPullingAlarm();
 
-        BinderCallsStatsService.Internal binderStats =
-                LocalServices.getService(BinderCallsStatsService.Internal.class);
-        if (binderStats != null) {
-            binderStats.reset();
-        }
-
-        LooperStats looperStats = LocalServices.getService(LooperStats.class);
-        if (looperStats != null) {
-            looperStats.reset();
-        }
         mStatsManagerService.statsdNotReady();
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
 
         synchronized (sStatsdLock) {
             writer.println(
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
index c1dc584..4e4bc40 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -30,7 +30,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
-import android.util.Slog;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -192,7 +192,7 @@
             statsd.registerPullAtomCallback(
                     callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
+            Log.e(TAG, "Failed to access statsd to register puller for atom " + atomTag);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -219,7 +219,7 @@
         try {
             statsd.unregisterPullAtomCallback(callingUid, atomTag);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
+            Log.e(TAG, "Failed to access statsd to unregister puller for atom " + atomTag);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -243,7 +243,7 @@
                 statsd.setDataFetchOperation(configId, pir, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to setDataFetchOperation with statsd");
+            Log.e(TAG, "Failed to setDataFetchOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -264,7 +264,7 @@
                 statsd.removeDataFetchOperation(configId, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to removeDataFetchOperation with statsd");
+            Log.e(TAG, "Failed to removeDataFetchOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -287,7 +287,7 @@
                 return statsd.setActiveConfigsChangedOperation(pir, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
+            Log.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -308,7 +308,7 @@
                 statsd.removeActiveConfigsChangedOperation(callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
+            Log.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -336,7 +336,7 @@
                         configId, subscriberId, pir, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to setBroadcastSubscriber with statsd");
+            Log.e(TAG, "Failed to setBroadcastSubscriber with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -362,7 +362,7 @@
                 statsd.unsetBroadcastSubscriber(configId, subscriberId, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
+            Log.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -378,7 +378,7 @@
                 return statsd.getRegisteredExperimentIds();
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
+            Log.e(TAG, "Failed to getRegisteredExperimentIds with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -396,7 +396,7 @@
                 return statsd.getMetadata();
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to getMetadata with statsd");
+            Log.e(TAG, "Failed to getMetadata with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -415,7 +415,7 @@
                 return statsd.getData(key, callingUid);
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to getData with statsd");
+            Log.e(TAG, "Failed to getData with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -436,7 +436,7 @@
                 return;
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to addConfiguration with statsd");
+            Log.e(TAG, "Failed to addConfiguration with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -457,7 +457,7 @@
                 return;
             }
         } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to removeConfiguration with statsd");
+            Log.e(TAG, "Failed to removeConfiguration with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -522,7 +522,7 @@
                 try {
                     mLock.wait(STATSD_TIMEOUT_MILLIS);
                 } catch (InterruptedException e) {
-                    Slog.e(TAG, "wait for statsd interrupted");
+                    Log.e(TAG, "wait for statsd interrupted");
                 }
             }
             return mStatsd;
@@ -578,7 +578,7 @@
             registerAllActiveConfigsChangedOperations(statsd);
             registerAllBroadcastSubscribers(statsd);
         } catch (RemoteException e) {
-            Slog.e(TAG, "StatsManager failed to (re-)register data with statsd");
+            Log.e(TAG, "StatsManager failed to (re-)register data with statsd");
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/apex/statsd/testing/Android.bp b/apex/statsd/testing/Android.bp
index 22e7301..a9cd0cc 100644
--- a/apex/statsd/testing/Android.bp
+++ b/apex/statsd/testing/Android.bp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-apex {
+apex_test {
     name: "test_com.android.os.statsd",
     visibility: [
         "//system/apex/tests",
diff --git a/api/current.txt b/api/current.txt
index d19eb0f..826d409 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2873,7 +2873,7 @@
     method public void onSystemActionsChanged();
     method public final boolean performGlobalAction(int);
     method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
-    method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
+    method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>);
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
     field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
     field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
@@ -2952,6 +2952,12 @@
     method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float);
   }
 
+  public static final class AccessibilityService.ScreenshotResult {
+    method @Nullable public android.graphics.ColorSpace getColorSpace();
+    method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer();
+    method public long getTimestamp();
+  }
+
   public static final class AccessibilityService.SoftKeyboardController {
     method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener);
     method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, @Nullable android.os.Handler);
@@ -4009,7 +4015,7 @@
     method public android.util.Size getAppTaskThumbnailSize();
     method public java.util.List<android.app.ActivityManager.AppTask> getAppTasks();
     method public android.content.pm.ConfigurationInfo getDeviceConfigurationInfo();
-    method @Nullable public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
+    method @NonNull public java.util.List<android.app.ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String, @IntRange(from=0) int, @IntRange(from=0) int);
     method public int getLargeMemoryClass();
     method public int getLauncherLargeIconDensity();
     method public int getLauncherLargeIconSize();
@@ -4549,12 +4555,13 @@
     method public int getPackageUid();
     method public int getPid();
     method @NonNull public String getProcessName();
-    method public int getPss();
+    method public long getPss();
     method public int getRealUid();
     method public int getReason();
-    method public int getRss();
+    method public long getRss();
     method public int getStatus();
     method public long getTimestamp();
+    method @NonNull public android.os.UserHandle getUserHandle();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationExitInfo> CREATOR;
     field public static final int REASON_ANR = 6; // 0x6
@@ -6810,7 +6817,7 @@
     ctor public DevicePolicyKeyguardService();
     method @Nullable public void dismiss();
     method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @Nullable public android.view.SurfaceControl onSurfaceReady(@Nullable android.os.IBinder);
+    method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage onSurfaceReady(@Nullable android.os.IBinder);
   }
 
   public class DevicePolicyManager {
@@ -29696,7 +29703,7 @@
     method public long getReportTimestamp();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
-    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped";
+    field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttempted";
     field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
     field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
     field public static final int NETWORK_PROBE_DNS = 4; // 0x4
@@ -30133,7 +30140,7 @@
     method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int);
     method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int);
     method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier);
-    method public void setOwnerUid(int);
+    method @NonNull public android.net.NetworkCapabilities setOwnerUid(int);
     method @NonNull public android.net.NetworkCapabilities setSignalStrength(int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
@@ -30773,7 +30780,6 @@
     method public void close();
     method public void continueCall(int) throws android.net.sip.SipException;
     method public void endCall() throws android.net.sip.SipException;
-    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
     method public android.net.sip.SipProfile getLocalProfile();
     method public android.net.sip.SipProfile getPeerProfile();
     method public int getState();
@@ -30784,7 +30790,6 @@
     method public void makeCall(android.net.sip.SipProfile, android.net.sip.SipSession, int) throws android.net.sip.SipException;
     method public void sendDtmf(int);
     method public void sendDtmf(int, android.os.Message);
-    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
     method public void setListener(android.net.sip.SipAudioCall.Listener);
     method public void setListener(android.net.sip.SipAudioCall.Listener, boolean);
     method public void setSpeakerMode(boolean);
@@ -30833,7 +30838,6 @@
     method public void close(String) throws android.net.sip.SipException;
     method public android.net.sip.SipSession createSipSession(android.net.sip.SipProfile, android.net.sip.SipSession.Listener) throws android.net.sip.SipException;
     method public static String getCallId(android.content.Intent);
-    method @NonNull public java.util.List<android.net.sip.SipProfile> getListOfProfiles() throws android.net.sip.SipException;
     method public static String getOfferSessionDescription(android.content.Intent);
     method public android.net.sip.SipSession getSessionFor(android.content.Intent) throws android.net.sip.SipException;
     method public static boolean isApiSupported(android.content.Context);
@@ -30851,11 +30855,6 @@
     method public void setRegistrationListener(String, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
     method public android.net.sip.SipAudioCall takeAudioCall(android.content.Intent, android.net.sip.SipAudioCall.Listener) throws android.net.sip.SipException;
     method public void unregister(android.net.sip.SipProfile, android.net.sip.SipRegistrationListener) throws android.net.sip.SipException;
-    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
-    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
-    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
-    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
-    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
     field public static final String EXTRA_CALL_ID = "android:sipCallID";
     field public static final String EXTRA_OFFER_SD = "android:sipOfferSD";
     field public static final int INCOMING_CALL_RESULT_CODE = 101; // 0x65
@@ -30865,7 +30864,6 @@
     method public int describeContents();
     method public String getAuthUserName();
     method public boolean getAutoRegistration();
-    method public int getCallingUid();
     method public String getDisplayName();
     method public String getPassword();
     method public int getPort();
@@ -31289,6 +31287,7 @@
     method public boolean isP2pSupported();
     method public boolean isPreferredNetworkOffloadSupported();
     method @Deprecated public boolean isScanAlwaysAvailable();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isScanThrottleEnabled();
     method public boolean isStaApConcurrencySupported();
     method public boolean isTdlsSupported();
     method public boolean isWapiSupported();
@@ -31761,7 +31760,7 @@
     field public static final int GROUP_OWNER_INTENT_MAX = 15; // 0xf
     field public static final int GROUP_OWNER_INTENT_MIN = 0; // 0x0
     field public String deviceAddress;
-    field public int groupOwnerIntent;
+    field @IntRange(from=0, to=15) public int groupOwnerIntent;
     field public android.net.wifi.WpsInfo wps;
   }
 
@@ -31953,14 +31952,14 @@
     method public int getDeviceType();
     method public int getMaxThroughput();
     method public boolean isContentProtectionSupported();
+    method public boolean isEnabled();
     method public boolean isSessionAvailable();
-    method public boolean isWfdEnabled();
     method public void setContentProtectionSupported(boolean);
-    method public void setControlPort(int);
+    method public void setControlPort(@IntRange(from=0) int);
     method public boolean setDeviceType(int);
-    method public void setMaxThroughput(int);
+    method public void setEnabled(boolean);
+    method public void setMaxThroughput(@IntRange(from=0) int);
     method public void setSessionAvailable(boolean);
-    method public void setWfdEnabled(boolean);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pWfdInfo> CREATOR;
     field public static final int DEVICE_TYPE_PRIMARY_SINK = 1; // 0x1
@@ -37039,6 +37038,15 @@
 
 }
 
+package android.os.ext.test {
+
+  @Deprecated public class Test {
+    ctor @Deprecated public Test();
+    method @Deprecated public void testE();
+  }
+
+}
+
 package android.os.health {
 
   public class HealthStats {
@@ -40409,6 +40417,7 @@
     field public static final String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
     field public static final String EXTRA_BIOMETRIC_MINIMUM_STRENGTH_REQUIRED = "android.provider.extra.BIOMETRIC_MINIMUM_STRENGTH_REQUIRED";
     field public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
+    field public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
     field public static final String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
     field public static final String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
     field public static final String EXTRA_EASY_CONNECT_ATTEMPTED_SSID = "android.provider.extra.EASY_CONNECT_ATTEMPTED_SSID";
@@ -43988,7 +43997,7 @@
     field public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
     field public static final String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
-    field public static final String META_DATA_BOOLEAN_TILE = "android.service.quicksettings.BOOLEAN_TILE";
+    field public static final String META_DATA_TOGGLEABLE_TILE = "android.service.quicksettings.TOGGLEABLE_TILE";
   }
 
 }
@@ -44069,8 +44078,8 @@
   }
 
   public static final class AlwaysOnHotwordDetector.ModelParamRange {
-    method public int end();
-    method public int start();
+    method public int getEnd();
+    method public int getStart();
   }
 
   public class VoiceInteractionService extends android.app.Service {
@@ -47305,7 +47314,7 @@
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
     method public void onUserMobileDataStateChanged(boolean);
     field public static final int LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE = 4194304; // 0x400000
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_BARRING_INFO = -2147483648; // 0x80000000
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
     field public static final int LISTEN_CALL_FORWARDING_INDICATOR = 8; // 0x8
     field public static final int LISTEN_CALL_STATE = 32; // 0x20
@@ -47318,7 +47327,7 @@
     field public static final int LISTEN_MESSAGE_WAITING_INDICATOR = 4; // 0x4
     field public static final int LISTEN_NONE = 0; // 0x0
     field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
-    field @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
+    field @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public static final int LISTEN_REGISTRATION_FAILURE = 1073741824; // 0x40000000
     field public static final int LISTEN_SERVICE_STATE = 1; // 0x1
     field @Deprecated public static final int LISTEN_SIGNAL_STRENGTH = 2; // 0x2
     field public static final int LISTEN_SIGNAL_STRENGTHS = 256; // 0x100
@@ -60828,7 +60837,6 @@
     method public void setTypeface(@Nullable android.graphics.Typeface, int);
     method public void setTypeface(@Nullable android.graphics.Typeface);
     method public void setWidth(int);
-    field public static final int ACCESSIBILITY_ACTION_IME_ENTER = 16908372; // 0x1020054
     field public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0; // 0x0
     field public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1; // 0x1
   }
@@ -60883,7 +60891,7 @@
     method public int getGravity();
     method public float getHorizontalMargin();
     method public float getVerticalMargin();
-    method @Deprecated public android.view.View getView();
+    method @Deprecated @Nullable public android.view.View getView();
     method public int getXOffset();
     method public int getYOffset();
     method public static android.widget.Toast makeText(android.content.Context, CharSequence, int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 90531b1..59aa145 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,4 +1,133 @@
 // Signature format: 2.0
+package android.net {
+
+  public final class TetheredClient implements android.os.Parcelable {
+    ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+    method public int describeContents();
+    method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+    method @NonNull public android.net.MacAddress getMacAddress();
+    method public int getTetheringType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+  }
+
+  public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.LinkAddress getAddress();
+    method @Nullable public String getHostname();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+  }
+
+  public class TetheringConstants {
+    ctor public TetheringConstants();
+    field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+    field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+    field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+    field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+    field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+  }
+
+  public class TetheringManager {
+    ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+    method public int getLastTetherError(@NonNull String);
+    method @NonNull public String[] getTetherableBluetoothRegexs();
+    method @NonNull public String[] getTetherableIfaces();
+    method @NonNull public String[] getTetherableUsbRegexs();
+    method @NonNull public String[] getTetherableWifiRegexs();
+    method @NonNull public String[] getTetheredIfaces();
+    method @NonNull public String[] getTetheringErroredIfaces();
+    method public boolean isTetheringSupported();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+    method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+    method @Deprecated public int setUsbTethering(boolean);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+    method @Deprecated public int tether(@NonNull String);
+    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+    method @Deprecated public int untether(@NonNull String);
+    field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+    field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+    field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+    field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+    field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+    field public static final int TETHERING_ETHERNET = 5; // 0x5
+    field public static final int TETHERING_INVALID = -1; // 0xffffffff
+    field public static final int TETHERING_NCM = 4; // 0x4
+    field public static final int TETHERING_USB = 1; // 0x1
+    field public static final int TETHERING_WIFI = 0; // 0x0
+    field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+    field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+    field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+    field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+    field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+    field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+    field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+    field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+    field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+    field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+    field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+    field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+    field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+    field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+    field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+  }
+
+  public static interface TetheringManager.OnTetheringEntitlementResultListener {
+    method public void onTetheringEntitlementResult(int);
+  }
+
+  public abstract static class TetheringManager.StartTetheringCallback {
+    ctor public TetheringManager.StartTetheringCallback();
+    method public void onTetheringFailed(int);
+    method public void onTetheringStarted();
+  }
+
+  public abstract static class TetheringManager.TetheringEventCallback {
+    ctor public TetheringManager.TetheringEventCallback();
+    method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+    method public void onError(@NonNull String, int);
+    method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+    method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+    method public void onTetheringSupported(boolean);
+    method public void onUpstreamChanged(@Nullable android.net.Network);
+  }
+
+  @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+    ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+    method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+  }
+
+  public static class TetheringManager.TetheringRequest {
+  }
+
+  public static class TetheringManager.TetheringRequest.Builder {
+    ctor public TetheringManager.TetheringRequest.Builder(int);
+    method @NonNull public android.net.TetheringManager.TetheringRequest build();
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+  }
+
+}
+
+package android.os.ext.test {
+
+  @Deprecated public class Test {
+    method @Deprecated public void testD();
+  }
+
+}
+
 package android.util {
 
   public final class Log {
diff --git a/api/system-current.txt b/api/system-current.txt
index 273309e..24936d5 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -585,10 +585,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR;
   }
 
-  public final class ApplicationExitInfo implements android.os.Parcelable {
-    method @NonNull public android.os.UserHandle getUserHandle();
-  }
-
   public class BroadcastOptions {
     method public static android.app.BroadcastOptions makeBasic();
     method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean);
@@ -2212,6 +2208,7 @@
     field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
     field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
     field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio";
+    field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery";
     field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final int FLAGS_PERMISSION_RESERVED_PERMISSIONCONTROLLER = -268435456; // 0xf0000000
@@ -3695,17 +3692,17 @@
   }
 
   public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable {
+    method public int getEnd();
+    method public int getStart();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR;
-    field public final int end;
-    field public final int start;
   }
 
   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 public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1
+    field public static final int AUDIO_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;
@@ -3865,6 +3862,20 @@
     method @NonNull public android.location.GnssReflectingPlane.Builder setLongitudeDegrees(@FloatRange(from=-180.0F, to=180.0f) double);
   }
 
+  public final class GnssRequest implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean isFullTracking();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssRequest> CREATOR;
+  }
+
+  public static final class GnssRequest.Builder {
+    ctor public GnssRequest.Builder();
+    ctor public GnssRequest.Builder(@NonNull android.location.GnssRequest);
+    method @NonNull public android.location.GnssRequest build();
+    method @NonNull public android.location.GnssRequest.Builder setFullTracking(boolean);
+  }
+
   public final class GnssSingleSatCorrection implements android.os.Parcelable {
     method public int describeContents();
     method @FloatRange(from=0.0f, fromInclusive=false) public float getCarrierFrequencyHz();
@@ -4138,6 +4149,7 @@
     method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
+    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.LOCATION_HARDWARE}) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent);
@@ -4215,15 +4227,15 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
   }
 
-  public final class AudioDeviceAddress implements android.os.Parcelable {
-    ctor public AudioDeviceAddress(@NonNull android.media.AudioDeviceInfo);
-    ctor public AudioDeviceAddress(int, int, @NonNull String);
+  public final class AudioDevice implements android.os.Parcelable {
+    ctor public AudioDevice(@NonNull android.media.AudioDeviceInfo);
+    ctor public AudioDevice(int, int, @NonNull String);
     method public int describeContents();
     method @NonNull public String getAddress();
     method public int getRole();
     method public int getType();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioDeviceAddress> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioDevice> CREATOR;
     field public static final int ROLE_INPUT = 1; // 0x1
     field public static final int ROLE_OUTPUT = 2; // 0x2
   }
@@ -4260,11 +4272,11 @@
     method @IntRange(from=0) public int getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     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 @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioDevice> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
     method @IntRange(from=0) public int getMaxAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
     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);
+    method @Nullable @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioDevice getPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getSupportedSystemUsages();
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes);
     method public boolean isAudioServerRunning();
@@ -4278,7 +4290,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) int);
     method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAddress);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDevice);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setSupportedSystemUsages(@NonNull int[]);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
@@ -4374,7 +4386,7 @@
 package android.media.audiofx {
 
   public class AudioEffect {
-    ctor @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public AudioEffect(@NonNull java.util.UUID, @NonNull android.media.AudioDeviceAddress);
+    ctor @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public AudioEffect(@NonNull java.util.UUID, @NonNull android.media.AudioDevice);
   }
 
 }
@@ -5745,7 +5757,6 @@
     method public int getFreqOffset();
     method public int getHierarchy();
     method @NonNull public boolean[] getLayerErrors();
-    method public int getLberCn();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
@@ -5757,37 +5768,32 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
-    method public int getVberCn();
-    method public int getXerCn();
     method public boolean isDemodLocked();
     method public boolean isEwbs();
     method public boolean isLnaOn();
     method public boolean isRfLock();
     field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe
-    field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 24; // 0x18
+    field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15
     field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2
     field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0
     field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd
     field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8
-    field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 21; // 0x15
-    field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 22; // 0x16
+    field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12
+    field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13
     field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10
-    field public static final int FRONTEND_STATUS_TYPE_LBER_CN = 18; // 0x12
     field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf
     field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb
-    field public static final int FRONTEND_STATUS_TYPE_MER = 20; // 0x14
+    field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11
     field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9
     field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3
     field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc
     field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4
-    field public static final int FRONTEND_STATUS_TYPE_RF_LOCK = 23; // 0x17
+    field public static final int FRONTEND_STATUS_TYPE_RF_LOCK = 20; // 0x14
     field public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY = 5; // 0x5
     field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
     field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
     field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
     field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
-    field public static final int FRONTEND_STATUS_TYPE_VBER_CN = 17; // 0x11
-    field public static final int FRONTEND_STATUS_TYPE_XER_CN = 19; // 0x13
   }
 
   public static class FrontendStatus.Atsc3PlpInfo {
@@ -6307,7 +6313,9 @@
     method @Nullable public String getSSID();
     method @NonNull public int[] getTransportTypes();
     method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
-    method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
+    method @NonNull public android.net.NetworkCapabilities setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
+    method @NonNull public android.net.NetworkCapabilities setRequestorPackageName(@NonNull String);
+    method @NonNull public android.net.NetworkCapabilities setRequestorUid(int);
     method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
     method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo);
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -6359,6 +6367,8 @@
   }
 
   public class NetworkRequest implements android.os.Parcelable {
+    method @Nullable public String getRequestorPackageName();
+    method public int getRequestorUid();
     method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
   }
 
@@ -6443,7 +6453,6 @@
   }
 
   public abstract class NetworkSpecifier {
-    method public void assertValidFromUid(int);
     method @Nullable public android.net.NetworkSpecifier redact();
     method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier);
   }
@@ -7206,6 +7215,28 @@
 
 }
 
+package android.net.sip {
+
+  public class SipAudioCall {
+    method @Nullable public android.net.rtp.AudioGroup getAudioGroup();
+    method public void setAudioGroup(@NonNull android.net.rtp.AudioGroup);
+  }
+
+  public class SipManager {
+    method @NonNull public java.util.List<android.net.sip.SipProfile> getProfiles() throws android.net.sip.SipException;
+    field public static final String ACTION_SIP_CALL_OPTION_CHANGED = "android.net.sip.action.SIP_CALL_OPTION_CHANGED";
+    field public static final String ACTION_SIP_INCOMING_CALL = "android.net.sip.action.SIP_INCOMING_CALL";
+    field public static final String ACTION_SIP_REMOVE_PROFILE = "android.net.sip.action.SIP_REMOVE_PROFILE";
+    field public static final String ACTION_SIP_SERVICE_UP = "android.net.sip.action.SIP_SERVICE_UP";
+    field public static final String ACTION_START_SIP = "android.net.sip.action.START_SIP";
+  }
+
+  public class SipProfile implements java.lang.Cloneable android.os.Parcelable java.io.Serializable {
+    method public int getCallingUid();
+  }
+
+}
+
 package android.net.util {
 
   public final class SocketUtils {
@@ -7689,6 +7720,7 @@
     method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setScanThrottleEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public boolean setSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setVerboseLoggingEnabled(boolean);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
@@ -7833,23 +7865,23 @@
     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 final class WifiOemMigrationHook {
+    method @Nullable public static android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData loadFromConfigStore();
   }
 
-  public static final class WifiOemConfigStoreMigrationHook.MigrationData implements android.os.Parcelable {
+  public static final class WifiOemMigrationHook.ConfigStoreMigrationData 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;
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData> 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 static final class WifiOemMigrationHook.ConfigStoreMigrationData.Builder {
+    ctor public WifiOemMigrationHook.ConfigStoreMigrationData.Builder();
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData build();
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSavedNetworkConfigurations(@NonNull java.util.List<android.net.wifi.WifiConfiguration>);
+    method @NonNull public android.net.wifi.WifiOemMigrationHook.ConfigStoreMigrationData.Builder setUserSoftApConfiguration(@NonNull android.net.wifi.SoftApConfiguration);
   }
 
   public class WifiScanner {
@@ -8107,7 +8139,7 @@
 
   public final class WifiP2pGroupList implements android.os.Parcelable {
     method public int describeContents();
-    method @NonNull public java.util.Collection<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
+    method @NonNull public java.util.List<android.net.wifi.p2p.WifiP2pGroup> getGroupList();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroupList> CREATOR;
   }
@@ -8115,12 +8147,13 @@
   public class WifiP2pManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void deletePersistentGroup(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void factoryReset(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
-    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void listen(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, boolean, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public void requestPersistentGroupInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.PersistentGroupInfoListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setDeviceName(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull String, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(allOf={android.Manifest.permission.CONNECTIVITY_INTERNAL, android.Manifest.permission.CONFIGURE_WIFI_DISPLAY}) public void setMiracastMode(int);
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) public void setWfdInfo(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @NonNull android.net.wifi.p2p.WifiP2pWfdInfo, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.OVERRIDE_WIFI_CONFIG}) public void setWifiP2pChannels(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, int, int, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void startListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void stopListening(@NonNull android.net.wifi.p2p.WifiP2pManager.Channel, @Nullable android.net.wifi.p2p.WifiP2pManager.ActionListener);
     field public static final String ACTION_WIFI_P2P_PERSISTENT_GROUPS_CHANGED = "android.net.wifi.p2p.action.WIFI_P2P_PERSISTENT_GROUPS_CHANGED";
     field public static final int MIRACAST_DISABLED = 0; // 0x0
     field public static final int MIRACAST_SINK = 2; // 0x2
@@ -9140,6 +9173,15 @@
 
 }
 
+package android.os.ext.test {
+
+  @Deprecated public class Test {
+    method @Deprecated public void testF();
+    method @Deprecated public void testG();
+  }
+
+}
+
 package android.os.image {
 
   public class DynamicSystemClient {
@@ -12420,7 +12462,6 @@
     field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
     field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
     field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
-    field public static final int DEFAULT_PREFERRED_NETWORK_MODE = 0; // 0x0
     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";
@@ -14126,6 +14167,10 @@
     method public boolean isContentCaptureFeatureEnabled();
   }
 
+  public abstract class ContentCaptureSession implements java.lang.AutoCloseable {
+    field public static final int NO_SESSION_ID = 0; // 0x0
+  }
+
   public final class ViewNode extends android.app.assist.AssistStructure.ViewNode {
     method @Nullable public android.view.autofill.AutofillId getParentAutofillId();
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index e1f8382..e352cb6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -923,6 +923,7 @@
     field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
+    field public static final int MODULE_APEX_NAME = 1; // 0x1
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
     field public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared";
   }
@@ -2539,6 +2540,7 @@
   }
 
   public class UserManager {
+    method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.CREATE_USERS"}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
     method public static boolean isSplitSystemUser();
     field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
   }
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index de1dbc9..c643b0e 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -37,6 +37,10 @@
 
 namespace utils {
 
+// Returns whether the Res_value::data_type represents a dynamic or regular resource reference.
+bool IsReference(uint8_t data_type);
+
+// Converts the Res_value::data_type to a human-readable string representation.
 StringPiece DataTypeToString(uint8_t data_type);
 
 struct OverlayManifestInfo {
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 4074789..43cfec3 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -27,6 +27,7 @@
 #include "idmap2/ResourceUtils.h"
 
 using android::base::StringPrintf;
+using android::idmap2::utils::IsReference;
 using android::idmap2::utils::ResToTypeEntryName;
 
 namespace android::idmap2 {
@@ -200,8 +201,7 @@
     // Only rewrite resources defined within the overlay package to their corresponding target
     // resource ids at runtime.
     bool rewrite_overlay_reference =
-        (overlay_resource->dataType == Res_value::TYPE_REFERENCE ||
-         overlay_resource->dataType == Res_value::TYPE_DYNAMIC_REFERENCE)
+        IsReference(overlay_resource->dataType)
             ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data)
             : false;
 
@@ -331,8 +331,13 @@
   std::unique_ptr<uint8_t[]> string_pool_data;
   Result<ResourceMapping> resource_mapping = {{}};
   if (overlay_info.resource_mapping != 0U) {
+    // Use the dynamic reference table to find the assigned resource id of the map xml.
+    const auto& ref_table = overlay_asset_manager.GetDynamicRefTableForCookie(0);
+    uint32_t resource_mapping_id = overlay_info.resource_mapping;
+    ref_table->lookupResourceId(&resource_mapping_id);
+
     // Load the overlay resource mappings from the file specified using android:resourcesMap.
-    auto asset = OpenNonAssetFromResource(overlay_info.resource_mapping, overlay_asset_manager);
+    auto asset = OpenNonAssetFromResource(resource_mapping_id, overlay_asset_manager);
     if (!asset) {
       return Error("failed opening xml for android:resourcesMap: %s",
                    asset.GetErrorMessage().c_str());
@@ -404,8 +409,7 @@
 
   target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
 
-  if (rewrite_overlay_reference &&
-      (data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+  if (rewrite_overlay_reference && IsReference(data_type)) {
     overlay_map_.insert(std::make_pair(data_value, target_resource));
   }
 
@@ -421,8 +425,7 @@
   const TargetValue value = target_iter->second;
   target_map_.erase(target_iter);
 
-  if (value.data_type != Res_value::TYPE_REFERENCE &&
-      value.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
+  if (!IsReference(value.data_type)) {
     return;
   }
 
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index a5df746..98d026b 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -33,6 +33,10 @@
 
 namespace android::idmap2::utils {
 
+bool IsReference(uint8_t data_type) {
+  return data_type == Res_value::TYPE_REFERENCE || data_type == Res_value::TYPE_DYNAMIC_REFERENCE;
+}
+
 StringPiece DataTypeToString(uint8_t data_type) {
   switch (data_type) {
     case Res_value::TYPE_NULL:
@@ -133,7 +137,7 @@
   }
 
   if (auto result_value = overlay_it->GetAttributeValue("resourcesMap")) {
-    if ((*result_value).dataType == Res_value::TYPE_REFERENCE) {
+    if (IsReference((*result_value).dataType)) {
       info.resource_mapping = (*result_value).data;
     } else {
       return Error("android:resourcesMap is not a reference in AndroidManifest.xml of %s",
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index f55acee..8af4037 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -56,12 +56,12 @@
     return type == DT_REG && path.size() > 4 && path.compare(path.size() - 4, 4, ".apk") == 0;
   });
   ASSERT_THAT(v, NotNull());
-  ASSERT_EQ(v->size(), 10U);
+  ASSERT_EQ(v->size(), 11U);
   ASSERT_EQ(std::set<std::string>(v->begin(), v->end()),
             std::set<std::string>(
                 {root + "/target/target.apk", root + "/target/target-no-overlayable.apk",
                  root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk",
-                 root + "/overlay/overlay-no-name-static.apk",
+                 root + "/overlay/overlay-no-name-static.apk", root + "/overlay/overlay-shared.apk",
                  root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk",
                  root + "/signature-overlay/signature-overlay.apk",
                  root + "/system-overlay/system-overlay.apk",
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 4bc6255..a2c1560 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -247,6 +247,43 @@
   ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x7f020002, 0x7f02000f);
 }
 
+TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
+  std::string target_apk_path = GetTestDataPath() + "/target/target.apk";
+  std::string overlay_apk_path = GetTestDataPath() + "/overlay/overlay-shared.apk";
+
+  std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path);
+  ASSERT_THAT(target_apk, NotNull());
+
+  std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path);
+  ASSERT_THAT(overlay_apk, NotNull());
+
+  auto idmap_result = Idmap::FromApkAssets(*target_apk, *overlay_apk, PolicyFlags::POLICY_PUBLIC,
+                                           /* enforce_overlayable */ true);
+  ASSERT_TRUE(idmap_result) << idmap_result.GetErrorMessage();
+  auto& idmap = *idmap_result;
+  ASSERT_THAT(idmap, NotNull());
+
+  const std::vector<std::unique_ptr<const IdmapData>>& dataBlocks = idmap->GetData();
+  ASSERT_EQ(dataBlocks.size(), 1U);
+
+  const std::unique_ptr<const IdmapData>& data = dataBlocks[0];
+  ASSERT_THAT(data, NotNull());
+
+  const auto& target_entries = data->GetTargetEntries();
+  ASSERT_EQ(target_entries.size(), 4U);
+  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00010000);
+  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020000);
+  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020001);
+  ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020002);
+
+  const auto& overlay_entries = data->GetOverlayEntries();
+  ASSERT_EQ(target_entries.size(), 4U);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x00010000, 0x7f010000);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x00020000, 0x7f02000c);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x00020001, 0x7f02000e);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x00020002, 0x7f02000f);
+}
+
 TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
   OverlayManifestInfo info{};
   info.target_package = "test.target";
diff --git a/cmds/idmap2/tests/data/overlay/build b/cmds/idmap2/tests/data/overlay/build
index b921b0d..114b099 100755
--- a/cmds/idmap2/tests/data/overlay/build
+++ b/cmds/idmap2/tests/data/overlay/build
@@ -51,4 +51,12 @@
     -o overlay-static-2.apk \
     compiled.flata
 
+aapt2 link \
+    --no-resource-removal \
+    --shared-lib \
+    -I "$FRAMEWORK_RES_APK" \
+    --manifest AndroidManifest.xml \
+    -o overlay-shared.apk \
+    compiled.flata
+
 rm compiled.flata
diff --git a/cmds/idmap2/tests/data/overlay/overlay-shared.apk b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
new file mode 100644
index 0000000..93dcc82
--- /dev/null
+++ b/cmds/idmap2/tests/data/overlay/overlay-shared.apk
Binary files differ
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 3a2472e..956fd29 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -44,12 +44,8 @@
 
 cc_defaults {
     name: "statsd_defaults",
-    aidl: {
-        include_dirs: ["frameworks/base/core/java"],
-    },
 
     srcs: [
-        ":statsd_aidl",
         "src/active_config_list.proto",
         "src/anomaly/AlarmMonitor.cpp",
         "src/anomaly/AlarmTracker.cpp",
@@ -118,22 +114,20 @@
     ],
 
     static_libs: [
-        "android.frameworks.stats@1.0",
         "libbase",
         "libcutils",
         "libprotoutil",
         "libstatslog",
         "libstatsmetadata",
         "libsysutils",
+        "libutils",
     ],
     shared_libs: [
         "libbinder",
-        "libhidlbase",
         "libincident",
         "liblog",
-        "libservices",
         "libstatssocket",
-        "libutils",
+        "statsd-aidl-cpp",
     ],
 }
 
@@ -222,8 +216,6 @@
 
     shared_libs: ["libgtest_prod"],
 
-    vintf_fragments: ["android.frameworks.stats@1.0-service.xml"],
-
     init_rc: ["statsd.rc"],
 }
 
diff --git a/cmds/statsd/android.frameworks.stats@1.0-service.xml b/cmds/statsd/android.frameworks.stats@1.0-service.xml
deleted file mode 100644
index bb02f66..0000000
--- a/cmds/statsd/android.frameworks.stats@1.0-service.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="framework">
-    <hal>
-        <name>android.frameworks.stats</name>
-        <transport>hwbinder</transport>
-        <version>1.0</version>
-        <interface>
-            <name>IStats</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-</manifest>
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 5b75b97..6b9d0e4 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -26,6 +26,86 @@
 using std::string;
 using std::vector;
 
+// These constants must be kept in sync with those in StatsDimensionsValue.java
+const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
+const static int STATS_DIMENSIONS_VALUE_INT_TYPE = 3;
+const static int STATS_DIMENSIONS_VALUE_LONG_TYPE = 4;
+// const static int STATS_DIMENSIONS_VALUE_BOOL_TYPE = 5; (commented out because
+// unused -- statsd does not correctly support bool types)
+const static int STATS_DIMENSIONS_VALUE_FLOAT_TYPE = 6;
+const static int STATS_DIMENSIONS_VALUE_TUPLE_TYPE = 7;
+
+/**
+ * Recursive helper function that populates a parent StatsDimensionsValueParcel
+ * with children StatsDimensionsValueParcels.
+ *
+ * \param dims vector of FieldValues stored by HashableDimensionKey
+ * \param index positions in dims vector to start reading children from
+ * \param depth level of parent parcel in the full StatsDimensionsValueParcel
+ * tree
+ */
+static void populateStatsDimensionsValueParcelChildren(StatsDimensionsValueParcel &parentParcel,
+                                                const vector<FieldValue>& dims, size_t& index,
+                                                int depth, int prefix) {
+    while (index < dims.size()) {
+        const FieldValue& dim = dims[index];
+        int fieldDepth = dim.mField.getDepth();
+        int fieldPrefix = dim.mField.getPrefix(depth);
+        StatsDimensionsValueParcel childParcel;
+        childParcel.field = dim.mField.getPosAtDepth(depth);
+        if (depth > 2) {
+            ALOGE("Depth > 2 not supported by StatsDimensionsValueParcel.");
+            return;
+        }
+        if (depth == fieldDepth && prefix == fieldPrefix) {
+            switch (dim.mValue.getType()) {
+                case INT:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_INT_TYPE;
+                    childParcel.intValue = dim.mValue.int_value;
+                    break;
+                case LONG:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_LONG_TYPE;
+                    childParcel.longValue = dim.mValue.long_value;
+                    break;
+                case FLOAT:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_FLOAT_TYPE;
+                    childParcel.floatValue = dim.mValue.float_value;
+                    break;
+                case STRING:
+                    childParcel.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
+                    childParcel.stringValue = String16(dim.mValue.str_value.c_str());
+                    break;
+                default:
+                    ALOGE("Encountered FieldValue with unsupported value type.");
+                    break;
+            }
+            index++;
+            parentParcel.tupleValue.push_back(childParcel);
+        } else if (fieldDepth > depth && fieldPrefix == prefix) {
+            childParcel.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+            populateStatsDimensionsValueParcelChildren(childParcel, dims, index, depth + 1,
+                                                       dim.mField.getPrefix(depth + 1));
+            parentParcel.tupleValue.push_back(childParcel);
+        } else {
+            return;
+        }
+    }
+}
+
+StatsDimensionsValueParcel HashableDimensionKey::toStatsDimensionsValueParcel() const {
+    StatsDimensionsValueParcel parcel;
+    if (mValues.size() == 0) {
+        return parcel;
+    }
+
+    parcel.field = mValues[0].mField.getTag();
+    parcel.valueType = STATS_DIMENSIONS_VALUE_TUPLE_TYPE;
+
+    size_t index = 0;
+    populateStatsDimensionsValueParcelChildren(parcel, mValues, index, /*depth=*/0, /*prefix=*/0);
+    return parcel;
+}
+
 android::hash_t hashDimension(const HashableDimensionKey& value) {
     android::hash_t hash = 0;
     for (const auto& fieldValue : value.getValues()) {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 654e135..4adcf96 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,10 +16,11 @@
 
 #pragma once
 
+#include <android/os/StatsDimensionsValueParcel.h>
 #include <utils/JenkinsHash.h>
 #include <vector>
-#include "FieldValue.h"
 #include "android-base/stringprintf.h"
+#include "FieldValue.h"
 #include "logd/LogEvent.h"
 
 namespace android {
@@ -69,6 +70,8 @@
         return nullptr;
     }
 
+    StatsDimensionsValueParcel toStatsDimensionsValueParcel() const;
+
     std::string toString() const;
 
     bool operator!=(const HashableDimensionKey& that) const;
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index bde15a5..6e7f081 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -196,17 +196,27 @@
         !checkPermissionForIds(kPermissionUsage, pid, uid)) {
         return;
     }
-    status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR, err4 = NO_ERROR;
-    string trainName = string(event->GetString(1 /*train name field id*/, &err));
-    int64_t trainVersionCode = event->GetLong(2 /*train version field id*/, &err2);
-    int32_t state = int32_t(event->GetLong(6 /*state field id*/, &err3));
+    // The Get* functions don't modify the status on success, they only write in
+    // failure statuses, so we can use one status variable for all calls then
+    // check if it is no longer NO_ERROR.
+    status_t err = NO_ERROR;
+    InstallTrainInfo trainInfo;
+    trainInfo.trainName = string(event->GetString(1 /*train name field id*/, &err));
+    trainInfo.trainVersionCode = event->GetLong(2 /*train version field id*/, &err);
+    trainInfo.requiresStaging = event->GetBool(3 /*requires staging field id*/, &err);
+    trainInfo.rollbackEnabled = event->GetBool(4 /*rollback enabled field id*/, &err);
+    trainInfo.requiresLowLatencyMonitor =
+            event->GetBool(5 /*requires low latency monitor field id*/, &err);
+    trainInfo.status = int32_t(event->GetLong(6 /*state field id*/, &err));
 #ifdef NEW_ENCODING_SCHEME
     std::vector<uint8_t> trainExperimentIdBytes =
-        event->GetStorage(7 /*experiment ids field id*/, &err4);
+            event->GetStorage(7 /*experiment ids field id*/, &err);
 #else
-    string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err4);
+    string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err);
 #endif
-    if (err != NO_ERROR || err2 != NO_ERROR || err3 != NO_ERROR || err4 != NO_ERROR) {
+    bool is_rollback = event->GetBool(10 /*is rollback field id*/, &err);
+
+    if (err != NO_ERROR) {
         ALOGE("Failed to parse fields in binary push state changed log event");
         return;
     }
@@ -220,83 +230,154 @@
         ALOGE("Failed to parse experimentids in binary push state changed.");
         return;
     }
-    vector<int64_t> experimentIdVector = {trainExperimentIds.experiment_id().begin(),
-                                          trainExperimentIds.experiment_id().end()};
+    trainInfo.experimentIds = {trainExperimentIds.experiment_id().begin(),
+                               trainExperimentIds.experiment_id().end()};
+
     // Update the train info on disk and get any data the logevent is missing.
-    getAndUpdateTrainInfoOnDisk(
-        state, &trainVersionCode, &trainName, &experimentIdVector);
+    getAndUpdateTrainInfoOnDisk(is_rollback, &trainInfo);
 
     std::vector<uint8_t> trainExperimentIdProto;
-    writeExperimentIdsToProto(experimentIdVector, &trainExperimentIdProto);
+    writeExperimentIdsToProto(trainInfo.experimentIds, &trainExperimentIdProto);
     int32_t userId = multiuser_get_user_id(uid);
 
-    event->updateValue(1 /*train name field id*/, trainName, STRING);
-    event->updateValue(2 /*train version field id*/, trainVersionCode, LONG);
+    event->updateValue(2 /*train version field id*/, trainInfo.trainVersionCode, LONG);
 #ifdef NEW_ENCODING_SCHEME
     event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE);
 #else
     event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STRING);
 #endif
     event->updateValue(8 /*user id field id*/, userId, INT);
+
+    if (is_rollback) {
+        int bit = trainInfo.requiresStaging ? 1 : 0;
+        event->updateValue(3 /*requires staging field id*/, bit, INT);
+        bit = trainInfo.rollbackEnabled ? 1 : 0;
+        event->updateValue(4 /*rollback enabled field id*/, bit, INT);
+        bit = trainInfo.requiresLowLatencyMonitor ? 1 : 0;
+        event->updateValue(5 /*requires low latency monitor field id*/, bit, INT);
+    }
 }
 
-void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(int32_t state,
-                                         int64_t* trainVersionCode,
-                                         string* trainName,
-                                         std::vector<int64_t>* experimentIds) {
+void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(bool is_rollback,
+                                                    InstallTrainInfo* trainInfo) {
+    // If the train name is empty, we don't know which train to attribute the
+    // event to, so return early.
+    if (trainInfo->trainName.empty()) {
+        return;
+    }
     bool readTrainInfoSuccess = false;
     InstallTrainInfo trainInfoOnDisk;
-    readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
+    readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo->trainName, trainInfoOnDisk);
 
     bool resetExperimentIds = false;
     if (readTrainInfoSuccess) {
         // Keep the old train version if we received an empty version.
-        if (*trainVersionCode == -1) {
-            *trainVersionCode = trainInfoOnDisk.trainVersionCode;
-        } else if (*trainVersionCode != trainInfoOnDisk.trainVersionCode) {
-        // Reset experiment ids if we receive a new non-empty train version.
-            resetExperimentIds = true;
-        }
-
-        // Keep the old train name if we received an empty train name.
-        if (trainName->size() == 0) {
-            *trainName = trainInfoOnDisk.trainName;
-        } else if (*trainName != trainInfoOnDisk.trainName) {
-            // Reset experiment ids if we received a new valid train name.
+        if (trainInfo->trainVersionCode == -1) {
+            trainInfo->trainVersionCode = trainInfoOnDisk.trainVersionCode;
+        } else if (trainInfo->trainVersionCode != trainInfoOnDisk.trainVersionCode) {
+            // Reset experiment ids if we receive a new non-empty train version.
             resetExperimentIds = true;
         }
 
         // Reset if we received a different experiment id.
-        if (!experimentIds->empty() &&
-                (trainInfoOnDisk.experimentIds.empty() ||
-                 experimentIds->at(0) != trainInfoOnDisk.experimentIds[0])) {
+        if (!trainInfo->experimentIds.empty() &&
+            (trainInfoOnDisk.experimentIds.empty() ||
+             trainInfo->experimentIds.at(0) != trainInfoOnDisk.experimentIds[0])) {
             resetExperimentIds = true;
         }
     }
 
     // Find the right experiment IDs
-    if (!resetExperimentIds && readTrainInfoSuccess) {
-        *experimentIds = trainInfoOnDisk.experimentIds;
+    if ((!resetExperimentIds || is_rollback) && readTrainInfoSuccess) {
+        trainInfo->experimentIds = trainInfoOnDisk.experimentIds;
     }
 
-    if (!experimentIds->empty()) {
-        int64_t firstId = experimentIds->at(0);
-        switch (state) {
+    if (!trainInfo->experimentIds.empty()) {
+        int64_t firstId = trainInfo->experimentIds.at(0);
+        switch (trainInfo->status) {
             case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS:
-                experimentIds->push_back(firstId + 1);
+                trainInfo->experimentIds.push_back(firstId + 1);
                 break;
             case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED:
-                experimentIds->push_back(firstId + 2);
+                trainInfo->experimentIds.push_back(firstId + 2);
                 break;
             case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS:
-                experimentIds->push_back(firstId + 3);
+                trainInfo->experimentIds.push_back(firstId + 3);
                 break;
         }
     }
 
-    StorageManager::writeTrainInfo(*trainVersionCode, *trainName, state, *experimentIds);
+    if (is_rollback) {
+        trainInfo->requiresStaging = trainInfoOnDisk.requiresStaging;
+        trainInfo->rollbackEnabled = trainInfoOnDisk.rollbackEnabled;
+        trainInfo->requiresLowLatencyMonitor = trainInfoOnDisk.requiresLowLatencyMonitor;
+    }
+
+    StorageManager::writeTrainInfo(*trainInfo);
 }
 
+void StatsLogProcessor::onWatchdogRollbackOccurredLocked(LogEvent* event) {
+    pid_t pid = event->GetPid();
+    uid_t uid = event->GetUid();
+    if (!checkPermissionForIds(kPermissionDump, pid, uid) ||
+        !checkPermissionForIds(kPermissionUsage, pid, uid)) {
+        return;
+    }
+    // The Get* functions don't modify the status on success, they only write in
+    // failure statuses, so we can use one status variable for all calls then
+    // check if it is no longer NO_ERROR.
+    status_t err = NO_ERROR;
+    int32_t rollbackType = int32_t(event->GetInt(1 /*rollback type field id*/, &err));
+    string packageName = string(event->GetString(2 /*package name field id*/, &err));
+
+    if (err != NO_ERROR) {
+        ALOGE("Failed to parse fields in watchdog rollback occurred log event");
+        return;
+    }
+
+    vector<int64_t> experimentIds =
+        processWatchdogRollbackOccurred(rollbackType, packageName);
+    vector<uint8_t> experimentIdProto;
+    writeExperimentIdsToProto(experimentIds, &experimentIdProto);
+
+#ifdef NEW_ENCODING_SCHEME
+    event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STORAGE);
+#else
+    event->updateValue(6 /*experiment ids field id*/, experimentIdProto, STRING);
+#endif
+}
+
+vector<int64_t> StatsLogProcessor::processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+                                                                    const string& packageNameIn) {
+    // If the package name is empty, we can't attribute it to any train, so
+    // return early.
+    if (packageNameIn.empty()) {
+      return vector<int64_t>();
+    }
+    bool readTrainInfoSuccess = false;
+    InstallTrainInfo trainInfoOnDisk;
+    readTrainInfoSuccess = StorageManager::readTrainInfo(packageNameIn, trainInfoOnDisk);
+
+    if (!readTrainInfoSuccess) {
+        return vector<int64_t>();
+    }
+
+    if (trainInfoOnDisk.experimentIds.empty()) {
+        return vector<int64_t>();
+    }
+    switch (rollbackTypeIn) {
+        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+            trainInfoOnDisk.experimentIds.push_back(trainInfoOnDisk.experimentIds[0] + 4);
+            StorageManager::writeTrainInfo(trainInfoOnDisk);
+            break;
+        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+            trainInfoOnDisk.experimentIds.push_back(trainInfoOnDisk.experimentIds[0] + 5);
+            StorageManager::writeTrainInfo(trainInfoOnDisk);
+            break;
+    }
+
+    return trainInfoOnDisk.experimentIds;
+}
 
 void StatsLogProcessor::resetConfigs() {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
@@ -321,7 +402,13 @@
     // Hard-coded logic to update train info on disk and fill in any information
     // this log event may be missing.
     if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) {
-      onBinaryPushStateChangedEventLocked(event);
+        onBinaryPushStateChangedEventLocked(event);
+    }
+
+    // Hard-coded logic to update experiment ids on disk for certain rollback
+    // types and fill the rollback atom with experiment ids
+    if (event->GetTagId() == android::util::WATCHDOG_ROLLBACK_OCCURRED) {
+        onWatchdogRollbackOccurredLocked(event);
     }
 
 #ifdef VERY_VERBOSE_PRINTING
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index c49f2e0..42e5676 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -18,6 +18,7 @@
 
 #include <gtest/gtest_prod.h>
 #include "config/ConfigListener.h"
+#include "logd/LogEvent.h"
 #include "metrics/MetricsManager.h"
 #include "packages/UidMap.h"
 #include "external/StatsPullerManager.h"
@@ -199,10 +200,18 @@
     // Handler over the binary push state changed event.
     void onBinaryPushStateChangedEventLocked(LogEvent* event);
 
+    // Handler over the watchdog rollback occurred event.
+    void onWatchdogRollbackOccurredLocked(LogEvent* event);
+
     // Updates train info on disk based on binary push state changed info and
     // write disk info into parameters.
-    void getAndUpdateTrainInfoOnDisk(int32_t state, int64_t* trainVersionCode,
-                                     string* trainName, std::vector<int64_t>* experimentIds);
+    void getAndUpdateTrainInfoOnDisk(bool is_rollback, InstallTrainInfo* trainInfoIn);
+
+    // Gets experiment ids on disk for associated train and updates them
+    // depending on rollback type. Then writes them back to disk and returns
+    // them.
+    std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
+                                                          const string& packageName);
 
     // Reset all configs.
     void resetConfigsLocked(const int64_t timestampNs);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 0256e36..168833f 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1293,182 +1293,27 @@
     return Status::ok();
 }
 
-Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn,
-                                                      const android::String16& packageNameIn,
-                                                      const int64_t packageVersionCodeIn,
-                                                      const int32_t rollbackReasonIn,
-                                                      const android::String16&
-                                                       failingPackageNameIn) {
-    // Note: We skip the usage stats op check here since we do not have a package name.
-    // This is ok since we are overloading the usage_stats permission.
-    // This method only sends data, it does not receive it.
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-    // Root, system, and shell always have access
-    if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
-        // Caller must be granted these permissions
-        if (!checkPermission(kPermissionDump)) {
-            return exception(binder::Status::EX_SECURITY,
-                             StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
-                                          kPermissionDump));
-        }
-        if (!checkPermission(kPermissionUsage)) {
-            return exception(binder::Status::EX_SECURITY,
-                             StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
-                                          kPermissionUsage));
-        }
-    }
-
-    android::util::stats_write(android::util::WATCHDOG_ROLLBACK_OCCURRED,
-            rollbackTypeIn, String8(packageNameIn).string(), packageVersionCodeIn,
-            rollbackReasonIn, String8(failingPackageNameIn).string());
-
-    // Fast return to save disk read.
-    if (rollbackTypeIn != android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS
-            && rollbackTypeIn !=
-                    android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE) {
-        return Status::ok();
-    }
-
-    bool readTrainInfoSuccess = false;
-    InstallTrainInfo trainInfoOnDisk;
-    readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk);
-
-    if (!readTrainInfoSuccess) {
-        return Status::ok();
-    }
-    std::vector<int64_t> experimentIds = trainInfoOnDisk.experimentIds;
-    if (experimentIds.empty()) {
-        return Status::ok();
-    }
-    switch (rollbackTypeIn) {
-        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
-            experimentIds.push_back(experimentIds[0] + 4);
-            break;
-        case android::util::WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
-            experimentIds.push_back(experimentIds[0] + 5);
-            break;
-    }
-    StorageManager::writeTrainInfo(trainInfoOnDisk.trainVersionCode, trainInfoOnDisk.trainName,
-            trainInfoOnDisk.status, experimentIds);
-    return Status::ok();
-}
-
-
 Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
     ENFORCE_UID(AID_SYSTEM);
     // TODO: add verifier permission
 
+    experimentIdsOut->clear();
     // Read the latest train info
-    InstallTrainInfo trainInfo;
-    if (!StorageManager::readTrainInfo(trainInfo)) {
+    vector<InstallTrainInfo> trainInfoList = StorageManager::readAllTrainInfo();
+    if (trainInfoList.empty()) {
         // No train info means no experiment IDs, return an empty list
-        experimentIdsOut->clear();
         return Status::ok();
     }
 
     // Copy the experiment IDs to the out vector
-    experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end());
+    for (InstallTrainInfo& trainInfo : trainInfoList) {
+        experimentIdsOut->insert(experimentIdsOut->end(),
+                                 trainInfo.experimentIds.begin(),
+                                 trainInfo.experimentIds.end());
+    }
     return Status::ok();
 }
 
-hardware::Return<void> StatsService::reportSpeakerImpedance(
-        const SpeakerImpedance& speakerImpedance) {
-    android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
-            speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
-    android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
-            hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportPhysicalDropDetected(
-        const PhysicalDropDetected& physicalDropDetected) {
-    android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED,
-            int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak,
-            physicalDropDetected.freefallDuration);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) {
-    std::vector<int32_t> buckets = chargeCycles.cycleBucket;
-    int initialSize = buckets.size();
-    for (int i = 0; i < 10 - initialSize; i++) {
-        buckets.push_back(-1); // Push -1 for buckets that do not exist.
-    }
-    android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
-            buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
-            buckets[9]);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportBatteryHealthSnapshot(
-        const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
-    android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT,
-            int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
-            batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
-            batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
-            batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) {
-    android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportBatteryCausedShutdown(
-        const BatteryCausedShutdown& batteryCausedShutdown) {
-    android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
-            batteryCausedShutdown.voltageMicroV);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportUsbPortOverheatEvent(
-        const UsbPortOverheatEvent& usbPortOverheatEvent) {
-    android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
-            usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
-            usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
-            usbPortOverheatEvent.timeToInactive);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportSpeechDspStat(
-        const SpeechDspStat& speechDspStat) {
-    android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
-            speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
-            speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
-
-    return hardware::Void();
-}
-
-hardware::Return<void> StatsService::reportVendorAtom(const VendorAtom& vendorAtom) {
-    std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName;
-    if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) {
-        ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId);
-        return hardware::Void();
-    }
-    if (reverseDomainName.length() > 50) {
-        ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str());
-        return hardware::Void();
-    }
-    LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), vendorAtom);
-    mProcessor->OnLogEvent(&event);
-
-    return hardware::Void();
-}
-
 void StatsService::binderDied(const wp <IBinder>& who) {
     ALOGW("statscompanion service died");
     StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index af3016f..0527d43 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,8 +27,6 @@
 #include "shell/ShellSubscriber.h"
 #include "statscompanion_util.h"
 
-#include <android/frameworks/stats/1.0/IStats.h>
-#include <android/frameworks/stats/1.0/types.h>
 #include <android/os/BnStatsd.h>
 #include <android/os/IPendingIntentRef.h>
 #include <android/os/IStatsCompanionService.h>
@@ -41,7 +39,6 @@
 
 using namespace android;
 using namespace android::binder;
-using namespace android::frameworks::stats::V1_0;
 using namespace android::os;
 using namespace std;
 
@@ -49,10 +46,7 @@
 namespace os {
 namespace statsd {
 
-using android::hardware::Return;
-
 class StatsService : public BnStatsd,
-                     public IStats,
                      public IBinder::DeathRecipient {
 public:
     StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
@@ -193,75 +187,10 @@
     virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override;
 
     /**
-     * Binder call to log WatchdogRollbackOccurred atom.
-     */
-    virtual Status sendWatchdogRollbackOccurredAtom(
-            const int32_t rollbackTypeIn,
-            const android::String16& packageNameIn,
-            const int64_t packageVersionCodeIn,
-            const int32_t rollbackReasonIn,
-            const android::String16& failingPackageNameIn) override;
-
-    /**
      * Binder call to get registered experiment IDs.
      */
     virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
 
-    /**
-     * Binder call to get SpeakerImpedance atom.
-     */
-    virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
-
-    /**
-     * Binder call to get HardwareFailed atom.
-     */
-    virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
-
-    /**
-     * Binder call to get PhysicalDropDetected atom.
-     */
-    virtual Return<void> reportPhysicalDropDetected(
-            const PhysicalDropDetected& physicalDropDetected) override;
-
-    /**
-     * Binder call to get ChargeCyclesReported atom.
-     */
-    virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
-
-    /**
-     * Binder call to get BatteryHealthSnapshot atom.
-     */
-    virtual Return<void> reportBatteryHealthSnapshot(
-            const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
-
-    /**
-     * Binder call to get SlowIo atom.
-     */
-    virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
-
-    /**
-     * Binder call to get BatteryCausedShutdown atom.
-     */
-    virtual Return<void> reportBatteryCausedShutdown(
-            const BatteryCausedShutdown& batteryCausedShutdown) override;
-
-    /**
-     * Binder call to get UsbPortOverheatEvent atom.
-     */
-    virtual Return<void> reportUsbPortOverheatEvent(
-            const UsbPortOverheatEvent& usbPortOverheatEvent) override;
-
-    /**
-     * Binder call to get Speech DSP state atom.
-     */
-    virtual Return<void> reportSpeechDspStat(
-            const SpeechDspStat& speechDspStat) override;
-
-    /**
-     * Binder call to get vendor atom.
-     */
-    virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override;
-
     /** IBinder::DeathRecipient */
     virtual void binderDied(const wp<IBinder>& who) override;
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 183d741..5e2dbf3 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -391,6 +391,7 @@
         WifiHealthStatReported wifi_health_stat_reported = 251 [(module) = "wifi"];
         WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"];
         WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
+        SdkExtensionStatus sdk_extension_status = 354;
     }
 
     // Pulled events will start at field 10000.
@@ -1873,6 +1874,8 @@
     // Set by RollbackPackageHealthObserver to be the package that is failing when a rollback
     // is initiated. Empty if the package is unknown.
     optional string failing_package_name = 5;
+
+    optional TrainExperimentIds experiment_ids = 6 [(log_mode) = MODE_BYTES];
 }
 
 /**
@@ -3769,6 +3772,8 @@
     // user id
     optional int32 user_id = 8;
     optional int32 reason = 9;
+    // Whether or not this is a rollback event
+    optional bool is_rollback = 10;
 }
 
 /* Test atom, is not logged anywhere */
@@ -8337,3 +8342,27 @@
     // Exception message (or log message) associated with the error (max 1000 chars)
     optional string exception_message = 2;
 }
+
+/**
+ * Logs when the SDK Extensions test app has polled the current version.
+ * This is atom ID 354.
+ *
+ * Logged from:
+ *   vendor/google_testing/integration/packages/apps/SdkExtensionsTestApp/
+ */
+message SdkExtensionStatus {
+    enum ApiCallStatus {
+        CALL_NOT_ATTEMPTED = 0;
+        CALL_SUCCESSFUL = 1;
+        CALL_FAILED = 2;
+    }
+
+    optional ApiCallStatus result = 1;
+
+    // The R extension version, i.e. android.os.ext.SdkExtension.getExtensionVersion(R).
+    optional int32 r_extension_version = 2;
+
+    // A number identifying which particular symbol's call failed, if any. 0 means no missing symbol.
+    // "Failed" here can mean a symbol that wasn't meant to be visible was, or the other way around.
+    optional int32 failed_call_symbol = 3;
+}
diff --git a/cmds/statsd/src/external/TrainInfoPuller.cpp b/cmds/statsd/src/external/TrainInfoPuller.cpp
index 9d09242..a7d8d4e 100644
--- a/cmds/statsd/src/external/TrainInfoPuller.cpp
+++ b/cmds/statsd/src/external/TrainInfoPuller.cpp
@@ -37,14 +37,16 @@
 }
 
 bool TrainInfoPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
-    InstallTrainInfo trainInfo;
-    bool ret = StorageManager::readTrainInfo(trainInfo);
-    if (!ret) {
-        ALOGW("Failed to read train info.");
-        return false;
+    vector<InstallTrainInfo> trainInfoList =
+        StorageManager::readAllTrainInfo();
+    if (trainInfoList.empty()) {
+        ALOGW("Train info was empty.");
+        return true;
     }
-    auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
-    data->push_back(event);
+    for (InstallTrainInfo& trainInfo : trainInfoList) {
+        auto event = make_shared<LogEvent>(getWallClockNs(), getElapsedRealtimeNs(), trainInfo);
+        data->push_back(event);
+    }
     return true;
 }
 
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index e8fc603..103eb0c 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -195,37 +195,6 @@
 }
 
 LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
-                   const VendorAtom& vendorAtom) {
-    mLogdTimestampNs = wallClockTimestampNs;
-    mElapsedTimestampNs = elapsedTimestampNs;
-    mTagId = vendorAtom.atomId;
-    mLogUid = AID_STATSD;
-
-    mValues.push_back(
-            FieldValue(Field(mTagId, getSimpleField(1)), Value(vendorAtom.reverseDomainName)));
-    for (int i = 0; i < (int)vendorAtom.values.size(); i++) {
-        switch (vendorAtom.values[i].getDiscriminator()) {
-            case VendorAtom::Value::hidl_discriminator::intValue:
-                mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
-                                             Value(vendorAtom.values[i].intValue())));
-                break;
-            case VendorAtom::Value::hidl_discriminator::longValue:
-                mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
-                                             Value(vendorAtom.values[i].longValue())));
-                break;
-            case VendorAtom::Value::hidl_discriminator::floatValue:
-                mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
-                                             Value(vendorAtom.values[i].floatValue())));
-                break;
-            case VendorAtom::Value::hidl_discriminator::stringValue:
-                mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)),
-                                             Value(vendorAtom.values[i].stringValue())));
-                break;
-        }
-    }
-}
-
-LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                    const InstallTrainInfo& trainInfo) {
     mLogdTimestampNs = wallClockTimestampNs;
     mElapsedTimestampNs = elapsedTimestampNs;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e4b784e..a67bef4 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -18,7 +18,6 @@
 
 #include "FieldValue.h"
 
-#include <android/frameworks/stats/1.0/types.h>
 #include <android/util/ProtoOutputStream.h>
 #include <private/android_logger.h>
 #include <stats_event_list.h>
@@ -27,8 +26,6 @@
 #include <string>
 #include <vector>
 
-using namespace android::frameworks::stats::V1_0;
-
 namespace android {
 namespace os {
 namespace statsd {
@@ -59,6 +56,9 @@
     std::string trainName;
     int32_t status;
     std::vector<int64_t> experimentIds;
+    bool requiresStaging;
+    bool rollbackEnabled;
+    bool requiresLowLatencyMonitor;
 };
 
 /**
@@ -103,9 +103,6 @@
                       const std::vector<uint8_t>& experimentIds, int32_t userId);
 
     explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
-                      const VendorAtom& vendorAtom);
-
-    explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
                       const InstallTrainInfo& installTrainInfo);
 
     ~LogEvent();
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 58bfeb3..140ef4e 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -23,7 +23,6 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/ProcessState.h>
-#include <hidl/HidlTransportSupport.h>
 #include <utils/Looper.h>
 
 #include <stdio.h>
@@ -75,8 +74,6 @@
     ps->giveThreadPoolName();
     IPCThreadState::self()->disableBackgroundScheduling(true);
 
-    ::android::hardware::configureRpcThreadpool(4 /*threads*/, false /*willJoin*/);
-
     std::shared_ptr<LogEventQueue> eventQueue =
             std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
 
@@ -89,12 +86,6 @@
         return -1;
     }
 
-    auto ret = gStatsService->registerAsService();
-    if (ret != ::android::OK) {
-        ALOGE("Failed to add service as HIDL service");
-        return 1; // or handle error
-    }
-
     registerSigHandler();
 
     gStatsService->sayHiToStatsCompanion();
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 507297c..1bac19e 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -44,7 +44,7 @@
 #define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"
 
 // Magic word at the start of the train info file, change this if changing the file format
-const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;
+const uint32_t TRAIN_INFO_FILE_MAGIC = 0xfb7447bf;
 
 // for ConfigMetricsReportList
 const int FIELD_ID_REPORTS = 2;
@@ -75,6 +75,29 @@
                         (long long)id);
 }
 
+static const char* findTrainInfoFileNameLocked(const string& trainName) {
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+    if (dir == NULL) {
+        VLOG("Path %s does not exist", TRAIN_INFO_DIR);
+        return nullptr;
+    }
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* fileName = de->d_name;
+        if (fileName[0] == '.') continue;
+
+        size_t fileNameLength = strlen(fileName);
+        if (fileNameLength >= trainName.length()) {
+            if (0 == strncmp(fileName + fileNameLength - trainName.length(), trainName.c_str(),
+                             trainName.length())) {
+                return fileName;
+            }
+        }
+    }
+
+    return nullptr;
+}
+
 // Returns array of int64_t which contains timestamp in seconds, uid,
 // configID and whether the file is a local history file.
 static void parseFileName(char* name, FileName* output) {
@@ -123,20 +146,25 @@
     close(fd);
 }
 
-bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
-                                    int32_t status, const std::vector<int64_t>& experimentIds) {
+bool StorageManager::writeTrainInfo(const InstallTrainInfo& trainInfo) {
     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
 
-    deleteAllFiles(TRAIN_INFO_DIR);
+    if (trainInfo.trainName.empty()) {
+      return false;
+    }
+    deleteSuffixedFiles(TRAIN_INFO_DIR, trainInfo.trainName.c_str());
 
-    int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
+    std::string fileName =
+            StringPrintf("%s/%ld_%s", TRAIN_INFO_DIR, (long) getWallClockSec(),
+                         trainInfo.trainName.c_str());
+
+    int fd = open(fileName.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
     if (fd == -1) {
-        VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
+        VLOG("Attempt to access %s but failed", fileName.c_str());
         return false;
     }
 
     size_t result;
-
     // Write the magic word
     result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
     if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
@@ -146,8 +174,8 @@
     }
 
     // Write the train version
-    const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
-    result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
+    const size_t trainVersionCodeByteCount = sizeof(trainInfo.trainVersionCode);
+    result = write(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
     if (result != trainVersionCodeByteCount) {
         VLOG("Failed to wrtie train version code");
         close(fd);
@@ -155,7 +183,7 @@
     }
 
     // Write # of bytes in trainName to file
-    const size_t trainNameSize = trainName.size();
+    const size_t trainNameSize = trainInfo.trainName.size();
     const size_t trainNameSizeByteCount = sizeof(trainNameSize);
     result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
     if (result != trainNameSizeByteCount) {
@@ -165,7 +193,7 @@
     }
 
     // Write trainName to file
-    result = write(fd, trainName.c_str(), trainNameSize);
+    result = write(fd, trainInfo.trainName.c_str(), trainNameSize);
     if (result != trainNameSize) {
         VLOG("Failed to write train name");
         close(fd);
@@ -173,8 +201,8 @@
     }
 
     // Write status to file
-    const size_t statusByteCount = sizeof(status);
-    result = write(fd, (uint8_t*)&status, statusByteCount);
+    const size_t statusByteCount = sizeof(trainInfo.status);
+    result = write(fd, (uint8_t*)&trainInfo.status, statusByteCount);
     if (result != statusByteCount) {
         VLOG("Failed to write status");
         close(fd);
@@ -182,7 +210,7 @@
     }
 
     // Write experiment id count to file.
-    const size_t experimentIdsCount = experimentIds.size();
+    const size_t experimentIdsCount = trainInfo.experimentIds.size();
     const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
     result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
     if (result != experimentIdsCountByteCount) {
@@ -193,7 +221,7 @@
 
     // Write experimentIds to file
     for (size_t i = 0; i < experimentIdsCount; i++) {
-        const int64_t experimentId = experimentIds[i];
+        const int64_t experimentId = trainInfo.experimentIds[i];
         const size_t experimentIdByteCount = sizeof(experimentId);
         result = write(fd, &experimentId, experimentIdByteCount);
         if (result == experimentIdByteCount) {
@@ -205,23 +233,47 @@
         }
     }
 
-    result = fchown(fd, AID_STATSD, AID_STATSD);
-    if (result) {
-        VLOG("Failed to chown train info file to statsd");
-        close(fd);
-        return false;
+    // Write bools to file
+    const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+    result = write(fd, (uint8_t*)&trainInfo.requiresStaging, boolByteCount);
+    if (result != boolByteCount) {
+      VLOG("Failed to write requires staging");
+      close(fd);
+      return false;
+    }
+
+    result = write(fd, (uint8_t*)&trainInfo.rollbackEnabled, boolByteCount);
+    if (result != boolByteCount) {
+      VLOG("Failed to write rollback enabled");
+      close(fd);
+      return false;
+    }
+
+    result = write(fd, (uint8_t*)&trainInfo.requiresLowLatencyMonitor, boolByteCount);
+    if (result != boolByteCount) {
+      VLOG("Failed to write requires log latency monitor");
+      close(fd);
+      return false;
     }
 
     close(fd);
     return true;
 }
 
-bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
+bool StorageManager::readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo) {
     std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+    return readTrainInfoLocked(trainName, trainInfo);
+}
 
-    int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
+bool StorageManager::readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo) {
+    trimToFit(TRAIN_INFO_DIR, /*parseTimestampOnly=*/ true);
+    const char* fileName = findTrainInfoFileNameLocked(trainName);
+    if (fileName == nullptr) {
+        return false;
+    }
+    int fd = open(StringPrintf("%s/%s", TRAIN_INFO_DIR, fileName).c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
-        VLOG("Failed to open train-info.bin");
+        VLOG("Failed to open %s", fileName);
         return false;
     }
 
@@ -297,6 +349,29 @@
         trainInfo.experimentIds.push_back(experimentId);
     }
 
+    // Read bools
+    const size_t boolByteCount = sizeof(trainInfo.requiresStaging);
+    result = read(fd, &trainInfo.requiresStaging, boolByteCount);
+    if (result != boolByteCount) {
+        VLOG("Failed to read requires requires staging from train info file");
+        close(fd);
+        return false;
+    }
+
+    result = read(fd, &trainInfo.rollbackEnabled, boolByteCount);
+    if (result != boolByteCount) {
+        VLOG("Failed to read requires rollback enabled from train info file");
+        close(fd);
+        return false;
+    }
+
+    result = read(fd, &trainInfo.requiresLowLatencyMonitor, boolByteCount);
+    if (result != boolByteCount) {
+        VLOG("Failed to read requires requires low latency monitor from train info file");
+        close(fd);
+        return false;
+    }
+
     // Expect to be at EOF.
     char c;
     result = read(fd, &c, 1);
@@ -311,6 +386,32 @@
     return true;
 }
 
+vector<InstallTrainInfo> StorageManager::readAllTrainInfo() {
+    std::lock_guard<std::mutex> lock(sTrainInfoMutex);
+    vector<InstallTrainInfo> trainInfoList;
+    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);
+    if (dir == NULL) {
+        VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
+        return trainInfoList;
+    }
+
+    dirent* de;
+    while ((de = readdir(dir.get()))) {
+        char* name = de->d_name;
+        if (name[0] == '.') {
+            continue;
+        }
+
+        InstallTrainInfo trainInfo;
+        bool readSuccess = StorageManager::readTrainInfoLocked(name, trainInfo);
+        if (!readSuccess) {
+            continue;
+        }
+        trainInfoList.push_back(trainInfo);
+    }
+    return trainInfoList;
+}
+
 void StorageManager::deleteFile(const char* file) {
     if (remove(file) != 0) {
         VLOG("Attempt to delete %s but is not found", file);
@@ -574,7 +675,7 @@
     });
 }
 
-void StorageManager::trimToFit(const char* path) {
+void StorageManager::trimToFit(const char* path, bool parseTimestampOnly) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);
     if (dir == NULL) {
         VLOG("Path %s does not exist", path);
@@ -589,7 +690,12 @@
         if (name[0] == '.') continue;
 
         FileName output;
-        parseFileName(name, &output);
+        if (parseTimestampOnly) {
+            output.mTimestampSec = StrToInt64(strtok(name, "_"));
+            output.mIsHistory = false;
+        } else {
+            parseFileName(name, &output);
+        }
         if (output.mTimestampSec == -1) continue;
         string file_name = output.getFullFileName(path);
 
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 69b41c2..d59046d 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -52,13 +52,22 @@
     /**
      * Writes train info.
      */
-    static bool writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
-                               int32_t status, const std::vector<int64_t>& experimentIds);
+    static bool writeTrainInfo(const InstallTrainInfo& trainInfo);
 
     /**
      * Reads train info.
      */
-    static bool readTrainInfo(InstallTrainInfo& trainInfo);
+    static bool readTrainInfo(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+    /**
+     * Reads train info assuming lock is obtained.
+     */
+    static bool readTrainInfoLocked(const std::string& trainName, InstallTrainInfo& trainInfo);
+
+    /**
+     * Reads all train info and returns a vector of train info.
+     */
+    static vector<InstallTrainInfo> readAllTrainInfo();
 
     /**
      * Reads the file content to the buffer.
@@ -124,7 +133,7 @@
      * Trims files in the provided directory to limit the total size, number of
      * files, accumulation of outdated files.
      */
-    static void trimToFit(const char* dir);
+    static void trimToFit(const char* dir, bool parseTimestampOnly = false);
 
     /**
      * Returns true if there already exists identical configuration on device.
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 160b57e..8fd6b46 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -121,7 +121,7 @@
             subscription.id(),
             subscription.rule_id(),
             cookies,
-            getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
+            dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
 }
 
 sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
@@ -138,61 +138,6 @@
     return pirMapIt->second;
 }
 
-void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
-                                   int prefix, vector<StatsDimensionsValue>* output) {
-    size_t count = dims.size();
-    while (*index < count) {
-        const auto& dim = dims[*index];
-        const int valueDepth = dim.mField.getDepth();
-        const int valuePrefix = dim.mField.getPrefix(depth);
-        if (valueDepth > 2) {
-            ALOGE("Depth > 2 not supported");
-            return;
-        }
-        if (depth == valueDepth && valuePrefix == prefix) {
-            switch (dim.mValue.getType()) {
-                case INT:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           dim.mValue.int_value));
-                    break;
-                case LONG:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           dim.mValue.long_value));
-                    break;
-                case FLOAT:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           dim.mValue.float_value));
-                    break;
-                case STRING:
-                    output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth),
-                                                           String16(dim.mValue.str_value.c_str())));
-                    break;
-                default:
-                    break;
-            }
-            (*index)++;
-        } else if (valueDepth > depth && valuePrefix == prefix) {
-            vector<StatsDimensionsValue> childOutput;
-            getStatsDimensionsValueHelper(dims, index, depth + 1, dim.mField.getPrefix(depth + 1),
-                                          &childOutput);
-            output->push_back(StatsDimensionsValue(dim.mField.getPosAtDepth(depth), childOutput));
-        } else {
-            return;
-        }
-    }
-}
-
-StatsDimensionsValue SubscriberReporter::getStatsDimensionsValue(const HashableDimensionKey& dim) {
-    if (dim.getValues().size() == 0) {
-        return StatsDimensionsValue();
-    }
-
-    vector<StatsDimensionsValue> fields;
-    size_t index = 0;
-    getStatsDimensionsValueHelper(dim.getValues(), &index, 0, 0, &fields);
-    return StatsDimensionsValue(dim.getValues()[0].mField.getTag(), fields);
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 087a1b8..42599f5 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -22,7 +22,6 @@
 
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // subscription
-#include "android/os/StatsDimensionsValue.h"
 #include "HashableDimensionKey.h"
 
 #include <mutex>
@@ -70,8 +69,6 @@
 
     sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
 
-    static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
-
 private:
     SubscriberReporter() {};
 
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index f4a59ed..9e69d97 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -290,33 +290,34 @@
     }
 }
 
-TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
-    HashableDimensionKey dim;
-
-    int pos1[] = {1, 1, 1};
-    int pos2[] = {1, 1, 2};
-    int pos3[] = {1, 1, 3};
-    int pos4[] = {2, 0, 0};
-
-    Field field1(10, pos1, 2);
-    Field field2(10, pos2, 2);
-    Field field3(10, pos3, 2);
-    Field field4(10, pos4, 0);
-
-    Value value1((int32_t)10025);
-    Value value2("tag");
-    Value value3((int32_t)987654);
-    Value value4((int32_t)99999);
-
-    dim.addValue(FieldValue(field1, value1));
-    dim.addValue(FieldValue(field2, value2));
-    dim.addValue(FieldValue(field3, value3));
-    dim.addValue(FieldValue(field4, value4));
-
-    SubscriberReporter::getStatsDimensionsValue(dim);
-    // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
-    // have any read api.
-}
+//TODO(b/149050405) Update this test for StatsDimensionValueParcel
+//TEST(AtomMatcherTest, TestSubscriberDimensionWrite) {
+//    HashableDimensionKey dim;
+//
+//    int pos1[] = {1, 1, 1};
+//    int pos2[] = {1, 1, 2};
+//    int pos3[] = {1, 1, 3};
+//    int pos4[] = {2, 0, 0};
+//
+//    Field field1(10, pos1, 2);
+//    Field field2(10, pos2, 2);
+//    Field field3(10, pos3, 2);
+//    Field field4(10, pos4, 0);
+//
+//    Value value1((int32_t)10025);
+//    Value value2("tag");
+//    Value value3((int32_t)987654);
+//    Value value4((int32_t)99999);
+//
+//    dim.addValue(FieldValue(field1, value1));
+//    dim.addValue(FieldValue(field2, value2));
+//    dim.addValue(FieldValue(field3, value3));
+//    dim.addValue(FieldValue(field4, value4));
+//
+//    SubscriberReporter::getStatsDimensionsValue(dim);
+//    // TODO(b/110562792): can't test anything here because StatsDimensionsValue class doesn't
+//    // have any read api.
+//}
 
 TEST(AtomMatcherTest, TestWriteDimensionToProto) {
     HashableDimensionKey dim;
diff --git a/cmds/statsd/tests/storage/StorageManager_test.cpp b/cmds/statsd/tests/storage/StorageManager_test.cpp
index b91e5a0..27a86e42 100644
--- a/cmds/statsd/tests/storage/StorageManager_test.cpp
+++ b/cmds/statsd/tests/storage/StorageManager_test.cpp
@@ -40,40 +40,12 @@
 
     bool result;
 
-    result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
-                                            trainInfo.status, trainInfo.experimentIds);
+    result = StorageManager::writeTrainInfo(trainInfo);
 
     EXPECT_TRUE(result);
 
     InstallTrainInfo trainInfoResult;
-    result = StorageManager::readTrainInfo(trainInfoResult);
-    EXPECT_TRUE(result);
-
-    EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
-    EXPECT_EQ(trainInfo.trainName.size(), trainInfoResult.trainName.size());
-    EXPECT_EQ(trainInfo.trainName, trainInfoResult.trainName);
-    EXPECT_EQ(trainInfo.status, trainInfoResult.status);
-    EXPECT_EQ(trainInfo.experimentIds.size(), trainInfoResult.experimentIds.size());
-    EXPECT_EQ(trainInfo.experimentIds, trainInfoResult.experimentIds);
-}
-
-TEST(StorageManagerTest, TrainInfoReadWriteEmptyTrainNameTest) {
-    InstallTrainInfo trainInfo;
-    trainInfo.trainVersionCode = 12345;
-    trainInfo.trainName = "";
-    trainInfo.status = 1;
-    const char* expIds = "test_ids";
-    trainInfo.experimentIds.assign(expIds, expIds + strlen(expIds));
-
-    bool result;
-
-    result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
-                                            trainInfo.status, trainInfo.experimentIds);
-
-    EXPECT_TRUE(result);
-
-    InstallTrainInfo trainInfoResult;
-    result = StorageManager::readTrainInfo(trainInfoResult);
+    result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
     EXPECT_TRUE(result);
 
     EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
@@ -94,13 +66,12 @@
 
     bool result;
 
-    result = StorageManager::writeTrainInfo(trainInfo.trainVersionCode, trainInfo.trainName,
-                                            trainInfo.status, trainInfo.experimentIds);
+    result = StorageManager::writeTrainInfo(trainInfo);
 
     EXPECT_TRUE(result);
 
     InstallTrainInfo trainInfoResult;
-    result = StorageManager::readTrainInfo(trainInfoResult);
+    result = StorageManager::readTrainInfo(trainInfo.trainName, trainInfoResult);
     EXPECT_TRUE(result);
 
     EXPECT_EQ(trainInfo.trainVersionCode, trainInfoResult.trainVersionCode);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 11f7f46..b65f68e 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -28,7 +28,9 @@
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
 import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
 import android.graphics.Region;
+import android.hardware.HardwareBuffer;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -585,7 +587,12 @@
     private FingerprintGestureController mFingerprintGestureController;
 
     /** @hide */
-    public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot";
+    public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER =
+            "screenshot_hardwareBuffer";
+
+    /** @hide */
+    public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID =
+            "screenshot_colorSpaceId";
 
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
@@ -1888,8 +1895,9 @@
     }
 
     /**
-     * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE}
-     * format.
+     * Takes a screenshot of the specified display and returns it via an
+     * {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer}
+     * to construct the bitmap from the ScreenshotResult's payload.
      * <p>
      * <strong>Note:</strong> In order to take screenshot your service has
      * to declare the capability to take screenshot by setting the
@@ -1907,7 +1915,7 @@
      * @return {@code true} if the taking screenshot accepted, {@code false} if not.
      */
     public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<Bitmap> callback) {
+            @NonNull Consumer<ScreenshotResult> callback) {
         Preconditions.checkNotNull(executor, "executor cannot be null");
         Preconditions.checkNotNull(callback, "callback cannot be null");
         final IAccessibilityServiceConnection connection =
@@ -1917,14 +1925,22 @@
             return false;
         }
         try {
-            connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> {
-                final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT);
-                final long identity = Binder.clearCallingIdentity();
-                try {
-                    executor.execute(() -> callback.accept(screenshot));
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
+            connection.takeScreenshot(displayId, new RemoteCallback((result) -> {
+                if (result == null) {
+                    sendScreenshotResult(executor, callback, null);
+                    return;
                 }
+                final HardwareBuffer hardwareBuffer =
+                        result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER);
+                final int colorSpaceId =
+                        result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID);
+                ColorSpace colorSpace = null;
+                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
+                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
+                }
+                ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
+                        colorSpace, System.currentTimeMillis());
+                sendScreenshotResult(executor, callback, screenshot);
             }));
         } catch (RemoteException re) {
             throw new RuntimeException(re);
@@ -2323,4 +2339,67 @@
             this.handler = handler;
         }
     }
+
+    private void sendScreenshotResult(Executor executor, Consumer<ScreenshotResult> callback,
+            ScreenshotResult screenshot) {
+        final ScreenshotResult result = screenshot;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            executor.execute(() -> callback.accept(result));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Class including hardwareBuffer, colorSpace, and timestamp to be the result for
+     * {@link AccessibilityService#takeScreenshot} API.
+     * <p>
+     * <strong>Note:</strong> colorSpace would be null if the name of this colorSpace isn't at
+     * {@link ColorSpace.Named}.
+     * </p>
+     */
+    public static final class ScreenshotResult {
+        private final @NonNull HardwareBuffer mHardwareBuffer;
+        private final @Nullable ColorSpace mColorSpace;
+        private final long mTimestamp;
+
+        private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer,
+                @Nullable ColorSpace colorSpace, long timestamp) {
+            Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null");
+            mHardwareBuffer = hardwareBuffer;
+            mColorSpace = colorSpace;
+            mTimestamp = timestamp;
+        }
+
+        /**
+         * Gets the colorSpace identifying a specific organization of colors of the screenshot.
+         *
+         * @return the colorSpace or {@code null} if the name of colorSpace isn't at
+         * {@link ColorSpace.Named}
+         */
+        @Nullable
+        public ColorSpace getColorSpace() {
+            return mColorSpace;
+        }
+
+        /**
+         * Gets the hardwareBuffer representing a memory buffer of the screenshot.
+         *
+         * @return the hardwareBuffer
+         */
+        @NonNull
+        public HardwareBuffer getHardwareBuffer() {
+            return mHardwareBuffer;
+        }
+
+        /**
+         * Gets the timestamp of taking the screenshot.
+         *
+         * @return the timestamp from {@link System#currentTimeMillis()}
+         */
+        public long getTimestamp() {
+            return mTimestamp;
+        };
+    }
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 5db4dd7..9177d4d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -110,7 +110,5 @@
 
     int getWindowIdForLeashToken(IBinder token);
 
-    Bitmap takeScreenshot(int displayId);
-
-    void takeScreenshotWithCallback(int displayId, in RemoteCallback callback);
+    void takeScreenshot(int displayId, in RemoteCallback callback);
 }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index dba3020..2838ad8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -94,6 +94,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
@@ -3555,13 +3556,13 @@
      * @return a list of {@link ApplicationExitInfo} records matching the criteria, sorted in
      *         the order from most recent to least recent.
      */
-    @Nullable
+    @NonNull
     public List<ApplicationExitInfo> getHistoricalProcessExitReasons(@Nullable String packageName,
             @IntRange(from = 0) int pid, @IntRange(from = 0) int maxNum) {
         try {
             ParceledListSlice<ApplicationExitInfo> r = getService().getHistoricalProcessExitReasons(
                     packageName, pid, maxNum, mContext.getUserId());
-            return r == null ? null : r.getList();
+            return r == null ? Collections.emptyList() : r.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index dfbaea1..fc37af9 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2854,7 +2854,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
          *
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessBackgroundTime(int)
@@ -2871,7 +2871,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground access
          *
          * @see #getLastAccessTime(int)
          * @see #getLastAccessBackgroundTime(int)
@@ -2889,7 +2889,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background access
          *
          * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
@@ -2906,7 +2906,7 @@
          *
          * @param flags The op flags
          *
-         * @return the last access event of {@code null}
+         * @return the last access event of {@code null} if there was no access
          */
         private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
                 @UidState int toUidState, @OpFlags int flags) {
@@ -2921,7 +2921,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
          *
          * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
@@ -2944,7 +2944,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection
          *
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectBackgroundTime(int)
@@ -2961,7 +2961,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectBackgroundTime(int)
@@ -2979,7 +2979,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
@@ -2996,8 +2996,7 @@
          *
          * @param flags The op flags
          *
-         * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * @return the last rejection event of {@code null} if there was no rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
@@ -3016,7 +3015,8 @@
          * @param toUidState The highest UID state for which to query (inclusive)
          * @param flags The op flags
          *
-         * @return the last access time (in milliseconds since epoch) or {@code -1}
+         * @return the last access time (in milliseconds since epoch) or {@code -1} if there was no
+         * rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
@@ -3039,7 +3039,7 @@
          *
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no rejection
          *
          * @see #getLastForegroundDuration(int)
          * @see #getLastBackgroundDuration(int)
@@ -3055,7 +3055,7 @@
          *
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no foreground rejection
          *
          * @see #getLastDuration(int)
          * @see #getLastBackgroundDuration(int)
@@ -3072,7 +3072,7 @@
          *
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no background rejection
          *
          * @see #getLastDuration(int)
          * @see #getLastForegroundDuration(int)
@@ -3091,7 +3091,7 @@
          * @param toUidState The highest UID state for which to query (inclusive)
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no rejection
          *
          * @see #getLastDuration(int)
          * @see #getLastForegroundDuration(int)
@@ -3115,7 +3115,7 @@
          *
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no proxy access
          *
          * @see #getLastForegroundProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
@@ -3132,7 +3132,7 @@
          *
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no proxy access
          *
          * @see #getLastProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
@@ -3150,7 +3150,7 @@
          *
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no proxy background access
          *
          * @see #getLastProxyInfo(int)
          * @see #getLastForegroundProxyInfo(int)
@@ -3170,7 +3170,7 @@
          * @param toUidState The highest UID state for which to query (inclusive)
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no proxy foreground access
          *
          * @see #getLastProxyInfo(int)
          * @see #getLastForegroundProxyInfo(int)
@@ -3426,7 +3426,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
          *
          * @see #getLastAccessForegroundTime(int)
          * @see #getLastAccessBackgroundTime(int)
@@ -3443,7 +3443,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground access
          *
          * @see #getLastAccessTime(int)
          * @see #getLastAccessBackgroundTime(int)
@@ -3461,7 +3461,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background access
          *
          * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
@@ -3478,7 +3478,7 @@
          *
          * @param flags The op flags
          *
-         * @return the last access event of {@code null}
+         * @return the last access event of {@code null} if there was no access
          */
         private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState,
                 @UidState int toUidState, @OpFlags int flags) {
@@ -3504,7 +3504,7 @@
          * @param flags The op flags
          *
          * @return the last access time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access
          *
          * @see #getLastAccessTime(int)
          * @see #getLastAccessForegroundTime(int)
@@ -3540,7 +3540,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection
          *
          * @see #getLastRejectForegroundTime(int)
          * @see #getLastRejectBackgroundTime(int)
@@ -3557,7 +3557,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectBackgroundTime(int)
@@ -3575,7 +3575,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
@@ -3592,7 +3592,7 @@
          *
          * @param flags The op flags
          *
-         * @return the last reject event of {@code null}
+         * @return the last reject event of {@code null} if there was no rejection
          */
         private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState,
                 @UidState int toUidState, @OpFlags int flags) {
@@ -3618,7 +3618,7 @@
          * @param flags The op flags
          *
          * @return the last rejection time (in milliseconds since epoch start (January 1, 1970
-         * 00:00:00.000 GMT - Gregorian)) or {@code -1}
+         * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection
          *
          * @see #getLastRejectTime(int)
          * @see #getLastRejectForegroundTime(int)
@@ -3662,7 +3662,7 @@
          *
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no access
          *
          * @see #getLastForegroundDuration(int)
          * @see #getLastBackgroundDuration(int)
@@ -3678,7 +3678,7 @@
          *
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no foreground access
          *
          * @see #getLastDuration(int)
          * @see #getLastBackgroundDuration(int)
@@ -3695,7 +3695,7 @@
          *
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no background access
          *
          * @see #getLastDuration(int)
          * @see #getLastForegroundDuration(int)
@@ -3714,7 +3714,7 @@
          * @param toUidState The highest UID state for which to query (inclusive)
          * @param flags The op flags
          *
-         * @return the duration in milliseconds or {@code -1}
+         * @return the duration in milliseconds or {@code -1} if there was no access
          *
          * @see #getLastDuration(int)
          * @see #getLastForegroundDuration(int)
@@ -3789,7 +3789,7 @@
          *
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no proxy access
          *
          * @see #getLastForegroundProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
@@ -3806,7 +3806,7 @@
          *
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no foreground proxy access
          *
          * @see #getLastProxyInfo(int)
          * @see #getLastBackgroundProxyInfo(int)
@@ -3824,7 +3824,7 @@
          *
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no background proxy access
          *
          * @see #getLastProxyInfo(int)
          * @see #getLastForegroundProxyInfo(int)
@@ -3844,7 +3844,7 @@
          * @param toUidState The highest UID state for which to query (inclusive)
          * @param flags The op flags
          *
-         * @return The proxy name or {@code null}
+         * @return The proxy info or {@code null} if there was no proxy access
          *
          * @see #getLastProxyInfo(int)
          * @see #getLastForegroundProxyInfo(int)
@@ -3916,7 +3916,10 @@
         }
 
         /**
-         * The features that have been used when checking the op
+         * The features that have been used when checking the op keyed by id of the feature.
+         *
+         * @see Context#createFeatureContext(String)
+         * @see #noteOp(String, int, String, String, String)
          */
         @DataClass.Generated.Member
         public @NonNull Map<String,OpFeatureEntry> getFeatures() {
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 4bf5f07..c55453e 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -19,7 +19,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
 import android.app.ActivityManager.RunningAppProcessInfo.Importance;
 import android.icu.text.SimpleDateFormat;
 import android.os.Parcel;
@@ -245,12 +244,12 @@
     /**
      * @see {@link #getPss}
      */
-    private int mPss;
+    private long mPss;
 
     /**
      * @see {@link #getRss}
      */
-    private int mRss;
+    private long mRss;
 
     /**
      * @see {@link #getTimestamp}
@@ -385,7 +384,7 @@
      * it's NOT the exact memory information prior to its death; and it'll be zero
      * if the process died before system had a chance to take the sample. </p>
      */
-    public int getPss() {
+    public long getPss() {
         return mPss;
     }
 
@@ -396,12 +395,13 @@
      * it's NOT the exact memory information prior to its death; and it'll be zero
      * if the process died before system had a chance to take the sample. </p>
      */
-    public int getRss() {
+    public long getRss() {
         return mRss;
     }
 
     /**
-     * The timestamp of the process's death, in milliseconds since the epoch.
+     * The timestamp of the process's death, in milliseconds since the epoch,
+     * as returned by {@link System#currentTimeMillis System.currentTimeMillis()}.
      */
     public long getTimestamp() {
         return mTimestamp;
@@ -409,6 +409,9 @@
 
     /**
      * The human readable description of the process's death, given by the system; could be null.
+     *
+     * <p class="note">Note: only intended to be human-readable and the system provides no
+     * guarantees that the format is stable across devices or Android releases.</p>
      */
     public @Nullable String getDescription() {
         return mDescription;
@@ -416,10 +419,7 @@
 
     /**
      * Return the user id of the record on a multi-user system.
-     *
-     * @hide
      */
-    @SystemApi
     public @NonNull UserHandle getUserHandle() {
         return UserHandle.of(UserHandle.getUserId(mRealUid));
     }
@@ -546,7 +546,7 @@
      *
      * @hide
      */
-    public void setPss(final int pss) {
+    public void setPss(final long pss) {
         mPss = pss;
     }
 
@@ -555,7 +555,7 @@
      *
      * @hide
      */
-    public void setRss(final int rss) {
+    public void setRss(final long rss) {
         mRss = rss;
     }
 
@@ -630,8 +630,8 @@
         dest.writeInt(mSubReason);
         dest.writeInt(mStatus);
         dest.writeInt(mImportance);
-        dest.writeInt(mPss);
-        dest.writeInt(mRss);
+        dest.writeLong(mPss);
+        dest.writeLong(mRss);
         dest.writeLong(mTimestamp);
         dest.writeString(mDescription);
     }
@@ -669,8 +669,8 @@
         mSubReason = in.readInt();
         mStatus = in.readInt();
         mImportance = in.readInt();
-        mPss = in.readInt();
-        mRss = in.readInt();
+        mPss = in.readLong();
+        mRss = in.readLong();
         mTimestamp = in.readLong();
         mDescription = in.readString();
     }
@@ -848,10 +848,10 @@
                     mImportance = proto.readInt(ApplicationExitInfoProto.IMPORTANCE);
                     break;
                 case (int) ApplicationExitInfoProto.PSS:
-                    mPss = proto.readInt(ApplicationExitInfoProto.PSS);
+                    mPss = proto.readLong(ApplicationExitInfoProto.PSS);
                     break;
                 case (int) ApplicationExitInfoProto.RSS:
-                    mRss = proto.readInt(ApplicationExitInfoProto.RSS);
+                    mRss = proto.readLong(ApplicationExitInfoProto.RSS);
                     break;
                 case (int) ApplicationExitInfoProto.TIMESTAMP:
                     mTimestamp = proto.readLong(ApplicationExitInfoProto.TIMESTAMP);
@@ -891,8 +891,8 @@
         result = 31 * result + mSubReason;
         result = 31 * result + mImportance;
         result = 31 * result + mStatus;
-        result = 31 * result + mPss;
-        result = 31 * result + mRss;
+        result = 31 * result + (int) mPss;
+        result = 31 * result + (int) mRss;
         result = 31 * result + Long.hashCode(mTimestamp);
         result = 31 * result + Objects.hashCode(mProcessName);
         result = 31 * result + Objects.hashCode(mDescription);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 16c0910..1d4a1ac 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -88,6 +88,7 @@
     void createNotificationChannelGroups(String pkg, in ParceledListSlice channelGroupList);
     void createNotificationChannels(String pkg, in ParceledListSlice channelsList);
     void createNotificationChannelsForPackage(String pkg, int uid, in ParceledListSlice channelsList);
+    ParceledListSlice getConversationsForPackage(String pkg, int uid);
     ParceledListSlice getNotificationChannelGroupsForPackage(String pkg, int uid, boolean includeDeleted);
     NotificationChannelGroup getNotificationChannelGroupForPackage(String groupId, String pkg, int uid);
     NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(String pkg, int uid, String groupId, boolean includeDeleted);
@@ -96,7 +97,7 @@
     NotificationChannel getNotificationChannel(String callingPkg, int userId, String pkg, String channelId);
     NotificationChannel getConversationNotificationChannel(String callingPkg, int userId, String pkg, String channelId, boolean returnParentIfNoConversationChannel, String conversationId);
     void createConversationNotificationChannelForPackage(String pkg, int uid, String triggeringKey, in NotificationChannel parentChannel, String conversationId);
-    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, boolean includeDeleted);
+    NotificationChannel getNotificationChannelForPackage(String pkg, int uid, String channelId, String conversationId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, String channelId);
     void deleteConversationNotificationChannels(String pkg, int uid, String conversationId);
     ParceledListSlice getNotificationChannels(String callingPkg, String targetPkg, int userId);
@@ -113,6 +114,7 @@
     int getAppsBypassingDndCount(int uid);
     ParceledListSlice getNotificationChannelsBypassingDnd(String pkg, int userId);
     boolean isPackagePaused(String pkg);
+    void deleteNotificationHistoryItem(String pkg, int uid, long postedTime);
 
     void silenceNotificationSound();
 
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
index 168f782..5d5956e 100644
--- a/core/java/android/app/ITaskOrganizerController.aidl
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -31,8 +31,19 @@
      */
     void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
 
-    /** Apply multiple WindowContainer operations at once. */
-    void applyContainerTransaction(in WindowContainerTransaction t);
+    /**
+     * Apply multiple WindowContainer operations at once.
+     * @param organizer If non-null this transaction will use the synchronization
+     *        scheme described in BLASTSyncEngine.java. The SurfaceControl transaction
+     *        containing the effects of this WindowContainer transaction will be passed
+     *        to the organizers Transaction ready callback. If null the transaction
+     *        will apply with non particular synchronization constraints (other than
+     *        it will all apply at once).
+     * @return If organizer was non-null returns an ID for the sync operation which will
+     *         later be passed to transactionReady. This lets TaskOrganizer implementations
+     *         differentiate overlapping sync operations.
+     */
+    int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer);
 
     /** Creates a persistent root task in WM for a particular windowing-mode. */
     ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode);
@@ -40,6 +51,9 @@
     /** Deletes a persistent root task in WM */
     boolean deleteRootTask(IWindowContainer task);
 
+    /** Gets direct child tasks (ordered from top-to-bottom) */
+    List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent);
+
     /** Get the root task which contains the current ime target */
     IWindowContainer getImeTarget(int display);
 
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 909a476..f26e628 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -346,6 +346,26 @@
     }
 
     /**
+     * Removes an individual historical notification and regenerates the string pool
+     */
+    public boolean removeNotificationFromWrite(String packageName, long postedTime) {
+        boolean removed = false;
+        for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
+            HistoricalNotification hn = mNotificationsToWrite.get(i);
+            if (packageName.equals(hn.getPackage())
+                    && postedTime == hn.getPostedTimeMs()) {
+                removed = true;
+                mNotificationsToWrite.remove(i);
+            }
+        }
+        if (removed) {
+            poolStringsFromNotifications();
+        }
+
+        return removed;
+    }
+
+    /**
      * Gets pooled strings in order to write them to disk
      */
     public @NonNull String[] getPooledStringsToWrite() {
diff --git a/core/java/android/app/WindowContext.java b/core/java/android/app/WindowContext.java
index 36ae450..d279983 100644
--- a/core/java/android/app/WindowContext.java
+++ b/core/java/android/app/WindowContext.java
@@ -60,12 +60,12 @@
         if (token != null && !isWindowToken(token)) {
             throw new IllegalArgumentException("Token must be registered to server.");
         }
+        mToken = token != null ? token : new Binder();
 
-        final ContextImpl contextImpl = createBaseWindowContext(base, token);
+        final ContextImpl contextImpl = createBaseWindowContext(base, mToken);
         attachBaseContext(contextImpl);
         contextImpl.setOuterContext(this);
 
-        mToken = token != null ? token : new Binder();
         mDisplayId = getDisplayId();
         mWindowManager = new WindowManagerImpl(this);
         mWindowManager.setDefaultToken(mToken);
diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java
index c2a76c5..2ac5ebf 100644
--- a/core/java/android/app/admin/DevicePolicyKeyguardService.java
+++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java
@@ -22,7 +22,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 
 /**
  * Client interface for providing the SystemUI with secondary lockscreen information.
@@ -43,14 +43,14 @@
         @Override
         public void onSurfaceReady(@Nullable IBinder hostInputToken, IKeyguardCallback callback) {
             mCallback = callback;
-            SurfaceControl surfaceControl =
+            SurfaceControlViewHost.SurfacePackage surfacePackage =
                     DevicePolicyKeyguardService.this.onSurfaceReady(hostInputToken);
 
             if (mCallback != null) {
                 try {
-                    mCallback.onSurfaceControlCreated(surfaceControl);
+                    mCallback.onRemoteContentReady(surfacePackage);
                 } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to return created SurfaceControl", e);
+                    Log.e(TAG, "Failed to return created SurfacePackage", e);
                 }
             }
         }
@@ -65,11 +65,11 @@
     /**
      * Called by keyguard once the host surface for the secondary lockscreen is ready to display
      * remote content.
-     * @return the {@link SurfaceControl} for the Surface the secondary lockscreen content is
-     *      attached to.
+     * @return the {@link SurfaceControlViewHost.SurfacePackage} for the Surface the
+     *      secondary lockscreen content is attached to.
      */
     @Nullable
-    public SurfaceControl onSurfaceReady(@Nullable IBinder hostInputToken) {
+    public SurfaceControlViewHost.SurfacePackage onSurfaceReady(@Nullable IBinder hostInputToken) {
         return null;
     }
 
diff --git a/core/java/android/app/admin/IKeyguardCallback.aidl b/core/java/android/app/admin/IKeyguardCallback.aidl
index 81e7d4d..856033d 100644
--- a/core/java/android/app/admin/IKeyguardCallback.aidl
+++ b/core/java/android/app/admin/IKeyguardCallback.aidl
@@ -15,13 +15,13 @@
  */
 package android.app.admin;
 
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 
 /**
  * Internal IPC interface for informing the keyguard of events on the secondary lockscreen.
  * @hide
  */
 interface IKeyguardCallback {
-    oneway void onSurfaceControlCreated(in SurfaceControl remoteSurfaceControl);
+    oneway void onRemoteContentReady(in SurfaceControlViewHost.SurfacePackage surfacePackage);
     oneway void onDismiss();
 }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index f32a4ab..0e0161f 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -700,6 +700,27 @@
     /** @hide */
     public static final String REMOTE_CALLBACK_RESULT = "result";
 
+    /**
+     * How long we wait for an attached process to publish its content providers
+     * before we decide it must be hung.
+     * @hide
+     */
+    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
+
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
+     * @hide
+     */
+    public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
+
+    // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
+    // long ActivityManagerService is giving a content provider to get published if a new process
+    // needs to be started for that.
+    private static final int GET_TYPE_TIMEOUT_MILLIS =
+            CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
+
     public ContentResolver(@Nullable Context context) {
         this(context, null);
     }
@@ -849,8 +870,6 @@
         }
     }
 
-    private static final int GET_TYPE_TIMEOUT_MILLIS = 3000;
-
     private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
         @GuardedBy("this")
         public boolean done;
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index 439d536..d25f413 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -332,9 +332,12 @@
          * Constructs a new {@link StringAtomicFormula} together with handling the necessary
          * hashing for the given key.
          *
-         * <p> The value will be hashed with SHA256 and the hex digest will be computed; for
-         * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value
-         * is less than 33 characters.
+         * <p> The value will be automatically hashed with SHA256 and the hex digest will be
+         * computed when the key is PACKAGE_NAME or INSTALLER_NAME and the value is more than 32
+         * characters.
+         *
+         * <p> The APP_CERTIFICATES and INSTALLER_CERTIFICATES are always delivered in hashed
+         * form. So the isHashedValue is set to true by default.
          *
          * @throws IllegalArgumentException if {@code key} cannot be used with string value.
          */
@@ -348,7 +351,10 @@
                     String.format(
                             "Key %s cannot be used with StringAtomicFormula", keyToString(key)));
             mValue = hashValue(key, value);
-            mIsHashedValue = !mValue.equals(value);
+            mIsHashedValue =
+                    key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE
+                            ? true
+                            : !mValue.equals(value);
         }
 
         StringAtomicFormula(Parcel in) {
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index b7afe3f..2ba2840 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -326,6 +326,7 @@
      */
     @RequiresPermission(
             allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES,
+                    android.Manifest.permission.UPDATE_APP_OPS_STATS,
                     android.Manifest.permission.INTERACT_ACROSS_USERS})
     public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) {
         try {
@@ -366,6 +367,7 @@
      */
     @RequiresPermission(
             allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES,
+                    android.Manifest.permission.UPDATE_APP_OPS_STATS,
                     android.Manifest.permission.INTERACT_ACROSS_USERS})
     public void resetInteractAcrossProfilesAppOps(
             @NonNull Collection<String> previousCrossProfilePackages,
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 660ed94..b5f4f80 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -98,4 +98,9 @@
             in ComponentName componentName, int flags, in IShortcutChangeCallback callback,
             int callbackId);
     void unregisterShortcutChangeCallback(String callingPackage, int callbackId);
+
+    void cacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
+            in UserHandle user);
+    void uncacheShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
+            in UserHandle user);
 }
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 7fda9aa..271d5e4 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -20,6 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
@@ -1089,6 +1090,61 @@
     }
 
     /**
+     * Mark shortcuts as cached for a package.
+     *
+     * <p>Only dynamic long lived shortcuts can be cached. None dynamic or non long lived shortcuts
+     * in the list will be ignored.
+     *
+     * <p>Unlike pinned shortcuts, where different callers can have different sets of pinned
+     * shortcuts, cached state is per shortcut only, and even if multiple callers cache the same
+     * shortcut, it can be uncached by any valid caller.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be cached.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
+    public void cacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.cacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove cached flag from shortcuts for a package.
+     *
+     * @param packageName The target package name.
+     * @param shortcutIds The IDs of the shortcut to be uncached.
+     * @param user The UserHandle of the profile.
+     * @throws IllegalStateException when the user is locked, or when the {@code user} user
+     * is locked or not running.
+     *
+     * @see ShortcutManager
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
+    public void uncacheShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
+            @NonNull UserHandle user) {
+        logErrorForInvalidProfileAccess(user);
+        try {
+            mService.uncacheShortcuts(mContext.getPackageName(), packageName, shortcutIds, user);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide kept for testing.
      */
     @Deprecated
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index 6743a3f..9735f81 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -74,6 +74,11 @@
     public boolean multiArch;
 
     /**
+     * The android:debuggable flag from the package manifest.
+     */
+    public boolean debuggable;
+
+    /**
      * Specifies the recommended install location. Can be one of
      * {@link PackageHelper#RECOMMEND_INSTALL_INTERNAL} to install on internal storage,
      * {@link PackageHelper#RECOMMEND_INSTALL_EXTERNAL} to install on external media,
@@ -108,6 +113,7 @@
         dest.writeInt(recommendedInstallLocation);
         dest.writeInt(installLocation);
         dest.writeInt(multiArch ? 1 : 0);
+        dest.writeInt(debuggable ? 1 : 0);
 
         if (verifiers == null || verifiers.length == 0) {
             dest.writeInt(0);
@@ -139,6 +145,7 @@
         recommendedInstallLocation = source.readInt();
         installLocation = source.readInt();
         multiArch = (source.readInt() != 0);
+        debuggable = (source.readInt() != 0);
 
         final int verifiersLength = source.readInt();
         if (verifiersLength == 0) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 5a0bcf0..e8668f1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -60,6 +60,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
 import android.util.AndroidException;
@@ -596,6 +597,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public static final int MODULE_APEX_NAME = 0x00000001;
 
     /** @hide */
@@ -2997,6 +2999,18 @@
     public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the requisite kernel support to support incremental delivery aka Incremental FileSystem.
+     *
+     * @see IncrementalManager#isEnabled
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_INCREMENTAL_DELIVERY =
+            "android.software.incremental_delivery";
+
+    /**
      * Extra field name for the URI to a verification file. Passed to a package
      * verifier.
      *
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index a11a1dd..a69905e 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -85,4 +85,11 @@
 
     public abstract boolean isForegroundDefaultLauncher(@NonNull String callingPackage,
             int callingUid);
+
+    public abstract void cacheShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
+    public abstract void uncacheShortcuts(int launcherUserId,
+            @NonNull String callingPackage, @NonNull String packageName,
+            @NonNull List<String> shortcutIds, int userId);
 }
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 73b8a48..7ebeb21 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -216,6 +216,10 @@
      * across device reboot, by simulating what happens on reboot without
      * actually rebooting the device.
      *
+     * Note rollbacks in the process of enabling will be lost after calling
+     * this method since they are not persisted yet. Don't call this method
+     * in the middle of the install process.
+     *
      * @throws SecurityException if the caller does not have appropriate permissions.
      *
      * @hide
diff --git a/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java b/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java
new file mode 100644
index 0000000..5544eae
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java
@@ -0,0 +1,79 @@
+/*
+ * 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.hardware.biometrics;
+
+import android.os.NativeHandle;
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
+
+/**
+ * A class that contains utilities for IBiometricNativeHandle.
+ *
+ * @hide
+ */
+public final class BiometricNativeHandleUtils {
+
+    private BiometricNativeHandleUtils() {
+    }
+
+    /**
+     * Converts a {@link NativeHandle} into an {@link IBiometricNativeHandle} by duplicating the
+     * underlying file descriptors.
+     *
+     * Both the original and new handle must be closed after use.
+     *
+     * @param h {@link NativeHandle}. Usually used to identify a WindowManager window. Can be null.
+     * @return A {@link IBiometricNativeHandle} representation of {@code h}. Will be null if
+     * {@code h} or its raw file descriptors are null.
+     */
+    public static IBiometricNativeHandle dup(NativeHandle h) {
+        IBiometricNativeHandle handle = null;
+        if (h != null && h.getFileDescriptors() != null && h.getInts() != null) {
+            handle = new IBiometricNativeHandle();
+            handle.ints = h.getInts().clone();
+            handle.fds = new ParcelFileDescriptor[h.getFileDescriptors().length];
+            for (int i = 0; i < h.getFileDescriptors().length; ++i) {
+                try {
+                    handle.fds[i] = ParcelFileDescriptor.dup(h.getFileDescriptors()[i]);
+                } catch (IOException e) {
+                    return null;
+                }
+            }
+        }
+        return handle;
+    }
+
+    /**
+     * Closes the handle's file descriptors.
+     *
+     * @param h {@link IBiometricNativeHandle} handle.
+     */
+    public static void close(IBiometricNativeHandle h) {
+        if (h != null) {
+            for (ParcelFileDescriptor fd : h.fds) {
+                if (fd != null) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                        // do nothing.
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl b/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl
new file mode 100644
index 0000000..6dcdc1b
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.hardware.biometrics;
+
+/**
+ * Representation of a native handle.
+ * Copied from /common/aidl/android/hardware/common/NativeHandle.aidl
+ * @hide
+ */
+parcelable IBiometricNativeHandle {
+    ParcelFileDescriptor[] fds;
+    int[] ints;
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 55ebe28..6bda46b 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -29,7 +29,9 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricNativeHandleUtils;
 import android.hardware.biometrics.CryptoObject;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.os.Binder;
 import android.os.CancellationSignal;
@@ -38,6 +40,7 @@
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.Looper;
+import android.os.NativeHandle;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.Trace;
@@ -245,6 +248,19 @@
     }
 
     /**
+     * Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback,
+     * int[], NativeHandle)} with {@code windowId} set to null.
+     *
+     * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[],
+     * NativeHandle)
+     */
+    @RequiresPermission(MANAGE_BIOMETRIC)
+    public void enroll(int userId, byte[] token, CancellationSignal cancel,
+            EnrollmentCallback callback, int[] disabledFeatures) {
+        enroll(userId, token, cancel, callback, disabledFeatures, null /* windowId */);
+    }
+
+    /**
      * Request face authentication enrollment. This call operates the face authentication hardware
      * and starts capturing images. Progress will be indicated by callbacks to the
      * {@link EnrollmentCallback} object. It terminates when
@@ -259,11 +275,13 @@
      * @param flags    optional flags
      * @param userId   the user to whom this face will belong to
      * @param callback an object to receive enrollment events
+     * @param windowId optional ID of a camera preview window for a single-camera device. Must be
+     *                 null if not used.
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
     public void enroll(int userId, byte[] token, CancellationSignal cancel,
-            EnrollmentCallback callback, int[] disabledFeatures) {
+            EnrollmentCallback callback, int[] disabledFeatures, @Nullable NativeHandle windowId) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an enrollment callback");
         }
@@ -278,20 +296,72 @@
         }
 
         if (mService != null) {
+            IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId);
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enroll");
                 mService.enroll(userId, mToken, token, mServiceReceiver,
-                        mContext.getOpPackageName(), disabledFeatures);
+                        mContext.getOpPackageName(), disabledFeatures, handle);
             } catch (RemoteException e) {
                 Log.w(TAG, "Remote exception in enroll: ", e);
-                if (callback != null) {
-                    // Though this may not be a hardware issue, it will cause apps to give up or
-                    // try again later.
-                    callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
-                            getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                // Though this may not be a hardware issue, it will cause apps to give up or
+                // try again later.
+                callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+                        getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
                                 0 /* vendorCode */));
-                }
+            } finally {
+                Trace.endSection();
+                BiometricNativeHandleUtils.close(handle);
+            }
+        }
+    }
+
+    /**
+     * Request face authentication enrollment for a remote client, for example Android Auto.
+     * This call operates the face authentication hardware and starts capturing images.
+     * Progress will be indicated by callbacks to the
+     * {@link EnrollmentCallback} object. It terminates when
+     * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+     * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param token    a unique token provided by a recent creation or verification of device
+     *                 credentials (e.g. pin, pattern or password).
+     * @param cancel   an object that can be used to cancel enrollment
+     * @param userId   the user to whom this face will belong to
+     * @param callback an object to receive enrollment events
+     * @hide
+     */
+    @RequiresPermission(MANAGE_BIOMETRIC)
+    public void enrollRemotely(int userId, byte[] token, CancellationSignal cancel,
+            EnrollmentCallback callback, int[] disabledFeatures) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an enrollment callback");
+        }
+
+        if (cancel != null) {
+            if (cancel.isCanceled()) {
+                Log.w(TAG, "enrollRemotely is already canceled.");
+                return;
+            } else {
+                cancel.setOnCancelListener(new OnEnrollCancelListener());
+            }
+        }
+
+        if (mService != null) {
+            try {
+                mEnrollmentCallback = callback;
+                Trace.beginSection("FaceManager#enrollRemotely");
+                mService.enrollRemotely(userId, mToken, token, mServiceReceiver,
+                        mContext.getOpPackageName(), disabledFeatures);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Remote exception in enrollRemotely: ", e);
+                // Though this may not be a hardware issue, it will cause apps to give up or
+                // try again later.
+                callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
+                        getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
+                                0 /* vendorCode */));
             } finally {
                 Trace.endSection();
             }
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 68a4aef..8ba2473 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.face;
 
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.face.IFaceServiceReceiver;
@@ -51,6 +52,10 @@
 
     // Start face enrollment
     void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+            String opPackageName, in int [] disabledFeatures, in IBiometricNativeHandle windowId);
+
+    // Start remote face enrollment
+    void enrollRemotely(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
             String opPackageName, in int [] disabledFeatures);
 
     // Cancel enrollment in progress
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index ff9d145..f6717c7 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -32,7 +32,9 @@
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricNativeHandleUtils;
 import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.os.Binder;
 import android.os.CancellationSignal;
@@ -41,6 +43,7 @@
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.Looper;
+import android.os.NativeHandle;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -403,15 +406,33 @@
     }
 
     /**
-     * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not
-     * display the BiometricPrompt.
-     * @param userId the user ID that the fingerprint hardware will authenticate for.
+     * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal, int,
+     * AuthenticationCallback, Handler, int, NativeHandle)} with {@code windowId} set to null.
+     *
+     * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal, int,
+     * AuthenticationCallback, Handler, int, NativeHandle)
+     *
      * @hide
      */
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
+        authenticate(crypto, cancel, flags, callback, handler, userId, null /* windowId */);
+    }
+
+    /**
+     * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
+     * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not
+     * display the BiometricPrompt.
+     * @param userId the user ID that the fingerprint hardware will authenticate for.
+     * @param windowId for optical fingerprint sensors that require active illumination by the OLED
+     *        display. Should be null for devices that don't require illumination.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
+    public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId,
+            @Nullable NativeHandle windowId) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -425,26 +446,44 @@
             }
         }
 
-        if (mService != null) try {
-            useHandler(handler);
-            mAuthenticationCallback = callback;
-            mCryptoObject = crypto;
-            long sessionId = crypto != null ? crypto.getOpId() : 0;
-            mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
-                    mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Remote exception while authenticating: ", e);
-            if (callback != null) {
+        if (mService != null) {
+            IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId);
+            try {
+                useHandler(handler);
+                mAuthenticationCallback = callback;
+                mCryptoObject = crypto;
+                long sessionId = crypto != null ? crypto.getOpId() : 0;
+                mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
+                        mContext.getOpPackageName(), handle);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Remote exception while authenticating: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or try
                 // again later.
                 callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
                         getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                            0 /* vendorCode */));
+                                0 /* vendorCode */));
+            } finally {
+                BiometricNativeHandleUtils.close(handle);
             }
         }
     }
 
     /**
+     * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int, int,
+     * EnrollmentCallback, NativeHandle)} with {@code windowId} set to null.
+     *
+     * @see FingerprintManager#enroll(byte[], CancellationSignal, int, int, EnrollmentCallback,
+     * NativeHandle)
+     *
+     * @hide
+     */
+    @RequiresPermission(MANAGE_FINGERPRINT)
+    public void enroll(byte [] token, CancellationSignal cancel, int flags,
+            int userId, EnrollmentCallback callback) {
+        enroll(token, cancel, flags, userId, callback, null /* windowId */);
+    }
+
+    /**
      * Request fingerprint enrollment. This call warms up the fingerprint hardware
      * and starts scanning for fingerprints. Progress will be indicated by callbacks to the
      * {@link EnrollmentCallback} object. It terminates when
@@ -462,7 +501,7 @@
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
     public void enroll(byte [] token, CancellationSignal cancel, int flags,
-            int userId, EnrollmentCallback callback) {
+            int userId, EnrollmentCallback callback, @Nullable NativeHandle windowId) {
         if (userId == UserHandle.USER_CURRENT) {
             userId = getCurrentUserId();
         }
@@ -479,18 +518,21 @@
             }
         }
 
-        if (mService != null) try {
-            mEnrollmentCallback = callback;
-            mService.enroll(mToken, token, userId, mServiceReceiver, flags,
-                    mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Remote exception in enroll: ", e);
-            if (callback != null) {
+        if (mService != null) {
+            IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId);
+            try {
+                mEnrollmentCallback = callback;
+                mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+                        mContext.getOpPackageName(), handle);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Remote exception in enroll: ", e);
                 // Though this may not be a hardware issue, it will cause apps to give up or try
                 // again later.
                 callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
                         getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE,
-                            0 /* vendorCode */));
+                                0 /* vendorCode */));
+            } finally {
+                BiometricNativeHandleUtils.close(handle);
             }
         }
     }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 1a7e128..f2ffd08d 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -15,6 +15,7 @@
  */
 package android.hardware.fingerprint;
 
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
@@ -31,7 +32,8 @@
     // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
     // through FingerprintManager now.
     void authenticate(IBinder token, long sessionId, int userId,
-            IFingerprintServiceReceiver receiver, int flags, String opPackageName);
+            IFingerprintServiceReceiver receiver, int flags, String opPackageName,
+            in IBiometricNativeHandle windowId);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
     // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
@@ -40,7 +42,7 @@
     // startPreparedClient().
     void prepareForAuthentication(IBinder token, long sessionId, int userId,
             IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie,
-            int callingUid, int callingPid, int callingUserId);
+            int callingUid, int callingPid, int callingUserId, in IBiometricNativeHandle windowId);
 
     // Starts authentication with the previously prepared client.
     void startPreparedClient(int cookie);
@@ -55,7 +57,7 @@
 
     // Start fingerprint enrollment
     void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
-            int flags, String opPackageName);
+            int flags, String opPackageName, in IBiometricNativeHandle windowId);
 
     // Cancel enrollment in progress
     void cancelEnrollment(IBinder token);
diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java
index a30fd6b..dbf33ca 100644
--- a/core/java/android/hardware/soundtrigger/ConversionUtil.java
+++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java
@@ -17,7 +17,6 @@
 package android.hardware.soundtrigger;
 
 import android.annotation.Nullable;
-import android.hardware.soundtrigger.ModelParams;
 import android.media.AudioFormat;
 import android.media.audio.common.AudioConfig;
 import android.media.soundtrigger_middleware.AudioCapabilities;
@@ -333,20 +332,22 @@
     public static int aidl2apiAudioCapabilities(int aidlCapabilities) {
         int result = 0;
         if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) {
-            result |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+            result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
         }
         if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) {
-            result |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+            result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
         }
         return result;
     }
 
     public static int api2aidlAudioCapabilities(int apiCapabilities) {
         int result = 0;
-        if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION) != 0) {
+        if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION)
+                != 0) {
             result |= AudioCapabilities.ECHO_CANCELLATION;
         }
-        if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION) != 0) {
+        if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_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 d505ae5..a74871d2 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -97,8 +97,8 @@
          */
         @Retention(RetentionPolicy.SOURCE)
         @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = {
-                CAPABILITY_ECHO_CANCELLATION,
-                CAPABILITY_NOISE_SUPPRESSION
+                AUDIO_CAPABILITY_ECHO_CANCELLATION,
+                AUDIO_CAPABILITY_NOISE_SUPPRESSION
         })
         public @interface AudioCapabilities {}
 
@@ -106,12 +106,12 @@
          * If set the underlying module supports AEC.
          * Describes bit field {@link ModuleProperties#audioCapabilities}
          */
-        public static final int CAPABILITY_ECHO_CANCELLATION = 0x1;
+        public static final int AUDIO_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;
+        public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2;
 
         /** Unique module ID provided by the native service */
         public final int id;
@@ -735,22 +735,40 @@
         /**
          * The inclusive start of supported range.
          */
-        public final int start;
+        private final int mStart;
 
         /**
          * The inclusive end of supported range.
          */
-        public final int end;
+        private final int mEnd;
 
         ModelParamRange(int start, int end) {
-            this.start = start;
-            this.end = end;
+            this.mStart = start;
+            this.mEnd = end;
         }
 
         /** @hide */
         private ModelParamRange(@NonNull Parcel in) {
-            this.start = in.readInt();
-            this.end = in.readInt();
+            this.mStart = in.readInt();
+            this.mEnd = in.readInt();
+        }
+
+        /**
+         * Get the beginning of the param range
+         *
+         * @return The inclusive start of the supported range.
+         */
+        public int getStart() {
+            return mStart;
+        }
+
+        /**
+         * Get the end of the param range
+         *
+         * @return The inclusive end of the supported range.
+         */
+        public int getEnd() {
+            return mEnd;
         }
 
         @NonNull
@@ -780,8 +798,8 @@
         public int hashCode() {
             final int prime = 31;
             int result = 1;
-            result = prime * result + (start);
-            result = prime * result + (end);
+            result = prime * result + (mStart);
+            result = prime * result + (mEnd);
             return result;
         }
 
@@ -797,10 +815,10 @@
                 return false;
             }
             ModelParamRange other = (ModelParamRange) obj;
-            if (start != other.start) {
+            if (mStart != other.mStart) {
                 return false;
             }
-            if (end != other.end) {
+            if (mEnd != other.mEnd) {
                 return false;
             }
             return true;
@@ -808,14 +826,14 @@
 
         @Override
         public void writeToParcel(@NonNull Parcel dest, int flags) {
-            dest.writeInt(start);
-            dest.writeInt(end);
+            dest.writeInt(mStart);
+            dest.writeInt(mEnd);
         }
 
         @Override
         @NonNull
         public String toString() {
-            return "ModelParamRange [start=" + start + ", end=" + end + "]";
+            return "ModelParamRange [start=" + mStart + ", end=" + mEnd + "]";
         }
     }
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 9bd3992..c1df5b6 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -221,10 +221,8 @@
     /**
      * Set a model specific {@link ModelParams} with the given value. This
      * parameter will keep its value for the duration the model is loaded regardless of starting
-     * and
-     * stopping recognition. Once the model is unloaded, the value will be lost.
-     * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before calling
-     * this method.
+     * and stopping recognition. Once the model is unloaded, the value will be lost.
+     * {@link #queryParameter} should be checked first before calling this method.
      *
      * @param soundModelHandle handle of model to apply parameter
      * @param modelParam       {@link ModelParams}
@@ -251,22 +249,14 @@
      * for the duration the model is loaded regardless of starting and stopping recognition.
      * Once the model is unloaded, the value will be lost. If the value is not set, a default
      * value is returned. See {@link ModelParams} for parameter default values.
-     * {@link SoundTriggerModule#queryParameter(int, int)} should be checked first before
+     * {@link #queryParameter} should be checked first before
      * calling this method. Otherwise, an exception can be thrown.
      *
      * @param soundModelHandle handle of model to get parameter
      * @param modelParam       {@link ModelParams}
      * @return value of parameter
-     * @throws UnsupportedOperationException if hal or model do not support this API.
-     *                                       {@link SoundTriggerModule#queryParameter(int, int)}
-     *                                       should
-     *                                       be checked first.
-     * @throws IllegalArgumentException      if invalid model handle or parameter is passed.
-     *                                       {@link SoundTriggerModule#queryParameter(int, int)}
-     *                                       should be checked first.
      */
-    public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam)
-            throws UnsupportedOperationException, IllegalArgumentException {
+    public synchronized int getParameter(int soundModelHandle, @ModelParams int modelParam) {
         try {
             return mService.getModelParameter(soundModelHandle,
                     ConversionUtil.api2aidlModelParameter(modelParam));
@@ -276,9 +266,8 @@
     }
 
     /**
-     * Determine if parameter control is supported for the given model handle.
-     * This method should be checked prior to calling {@link SoundTriggerModule#setParameter} or
-     * {@link SoundTriggerModule#getParameter}.
+     * Query the parameter support and range for a given {@link ModelParams}.
+     * This method should be check prior to calling {@link #setParameter} or {@link #getParameter}.
      *
      * @param soundModelHandle handle of model to get parameter
      * @param modelParam       {@link ModelParams}
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index b128ea7..3c39d15 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -202,7 +202,7 @@
          */
         @NetworkProbe
         public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
-                "networkProbesAttemped";
+                "networkProbesAttempted";
 
         /** @hide */
         @StringDef(prefix = {"KEY_"}, value = {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fa12c08..a305948 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2423,14 +2423,14 @@
     /**
      * Get the set of tethered dhcp ranges.
      *
-     * @return an array of 0 or more {@code String} of tethered dhcp ranges.
-     * @deprecated This API just return the default value which is not used in DhcpServer.
+     * @deprecated This method is not supported.
+     * TODO: remove this function when all of clients are removed.
      * {@hide}
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     @Deprecated
     public String[] getTetheredDhcpRanges() {
-        return getTetheringManager().getTetheredDhcpRanges();
+        throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
     }
 
     /**
@@ -3750,6 +3750,7 @@
         checkCallbackNotNull(callback);
         Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities");
         final NetworkRequest request;
+        final String callingPackageName = mContext.getOpPackageName();
         try {
             synchronized(sCallbacks) {
                 if (callback.networkRequest != null
@@ -3761,10 +3762,11 @@
                 Messenger messenger = new Messenger(handler);
                 Binder binder = new Binder();
                 if (action == LISTEN) {
-                    request = mService.listenForNetwork(need, messenger, binder);
+                    request = mService.listenForNetwork(
+                            need, messenger, binder, callingPackageName);
                 } else {
                     request = mService.requestNetwork(
-                            need, messenger, timeoutMs, binder, legacyType);
+                            need, messenger, timeoutMs, binder, legacyType, callingPackageName);
                 }
                 if (request != null) {
                     sCallbacks.put(request, callback);
@@ -4037,8 +4039,10 @@
             @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
+        final String callingPackageName = mContext.getOpPackageName();
         try {
-            mService.pendingRequestForNetwork(request.networkCapabilities, operation);
+            mService.pendingRequestForNetwork(
+                    request.networkCapabilities, operation, callingPackageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -4150,8 +4154,10 @@
             @NonNull PendingIntent operation) {
         printStackTrace();
         checkPendingIntentNotNull(operation);
+        final String callingPackageName = mContext.getOpPackageName();
         try {
-            mService.pendingListenForNetwork(request.networkCapabilities, operation);
+            mService.pendingListenForNetwork(
+                    request.networkCapabilities, operation, callingPackageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 0fae607..1c7628f 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -167,18 +167,19 @@
             in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
+            in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
+            String callingPackageName);
 
     NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation);
+            in PendingIntent operation, String callingPackageName);
 
     void releasePendingNetworkRequest(in PendingIntent operation);
 
     NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
-            in Messenger messenger, in IBinder binder);
+            in Messenger messenger, in IBinder binder, String callingPackageName);
 
     void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
-            in PendingIntent operation);
+            in PendingIntent operation, String callingPackageName);
 
     void releaseNetworkRequest(in NetworkRequest networkRequest);
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index cf5f225..f8b51dd 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -27,6 +27,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.proto.ProtoOutputStream;
 
@@ -63,6 +64,16 @@
     // Set to true when private DNS is broken.
     private boolean mPrivateDnsBroken;
 
+    /**
+     * Uid of the app making the request.
+     */
+    private int mRequestorUid;
+
+    /**
+     * Package name of the app making the request.
+     */
+    private String mRequestorPackageName;
+
     public NetworkCapabilities() {
         clearAll();
         mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -89,6 +100,8 @@
         mOwnerUid = Process.INVALID_UID;
         mSSID = null;
         mPrivateDnsBroken = false;
+        mRequestorUid = Process.INVALID_UID;
+        mRequestorPackageName = null;
     }
 
     /**
@@ -109,6 +122,8 @@
         mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
         mSSID = nc.mSSID;
         mPrivateDnsBroken = nc.mPrivateDnsBroken;
+        mRequestorUid = nc.mRequestorUid;
+        mRequestorPackageName = nc.mRequestorPackageName;
     }
 
     /**
@@ -810,7 +825,7 @@
     }
 
     /**
-     * UID of the app that owns this network, or INVALID_UID if none/unknown.
+     * UID of the app that owns this network, or Process#INVALID_UID if none/unknown.
      *
      * <p>This field keeps track of the UID of the app that created this network and is in charge of
      * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
@@ -821,8 +836,9 @@
     /**
      * Set the UID of the owner app.
      */
-    public void setOwnerUid(final int uid) {
+    public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
         mOwnerUid = uid;
+        return this;
     }
 
     /**
@@ -865,9 +881,11 @@
      * @hide
      */
     @SystemApi
-    public void setAdministratorUids(@NonNull final List<Integer> administratorUids) {
+    public @NonNull NetworkCapabilities setAdministratorUids(
+            @NonNull final List<Integer> administratorUids) {
         mAdministratorUids.clear();
         mAdministratorUids.addAll(administratorUids);
+        return this;
     }
 
     /**
@@ -1385,6 +1403,7 @@
         combineSignalStrength(nc);
         combineUids(nc);
         combineSSIDs(nc);
+        combineRequestor(nc);
     }
 
     /**
@@ -1404,7 +1423,8 @@
                 && satisfiedBySpecifier(nc)
                 && (onlyImmutable || satisfiedBySignalStrength(nc))
                 && (onlyImmutable || satisfiedByUids(nc))
-                && (onlyImmutable || satisfiedBySSID(nc)));
+                && (onlyImmutable || satisfiedBySSID(nc)))
+                && (onlyImmutable || satisfiedByRequestor(nc));
     }
 
     /**
@@ -1488,7 +1508,7 @@
     public boolean equals(@Nullable Object obj) {
         if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
         NetworkCapabilities that = (NetworkCapabilities) obj;
-        return (equalsNetCapabilities(that)
+        return equalsNetCapabilities(that)
                 && equalsTransportTypes(that)
                 && equalsLinkBandwidths(that)
                 && equalsSignalStrength(that)
@@ -1496,7 +1516,8 @@
                 && equalsTransportInfo(that)
                 && equalsUids(that)
                 && equalsSSID(that)
-                && equalsPrivateDnsBroken(that));
+                && equalsPrivateDnsBroken(that)
+                && equalsRequestor(that);
     }
 
     @Override
@@ -1514,7 +1535,9 @@
                 + Objects.hashCode(mUids) * 31
                 + Objects.hashCode(mSSID) * 37
                 + Objects.hashCode(mTransportInfo) * 41
-                + Objects.hashCode(mPrivateDnsBroken) * 43;
+                + Objects.hashCode(mPrivateDnsBroken) * 43
+                + Objects.hashCode(mRequestorUid) * 47
+                + Objects.hashCode(mRequestorPackageName) * 53;
     }
 
     @Override
@@ -1537,6 +1560,8 @@
         dest.writeBoolean(mPrivateDnsBroken);
         dest.writeList(mAdministratorUids);
         dest.writeInt(mOwnerUid);
+        dest.writeInt(mRequestorUid);
+        dest.writeString(mRequestorPackageName);
     }
 
     public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1559,6 +1584,8 @@
                 netCap.mPrivateDnsBroken = in.readBoolean();
                 netCap.setAdministratorUids(in.readArrayList(null));
                 netCap.mOwnerUid = in.readInt();
+                netCap.mRequestorUid = in.readInt();
+                netCap.mRequestorPackageName = in.readString();
                 return netCap;
             }
             @Override
@@ -1624,6 +1651,9 @@
             sb.append(" Private DNS is broken");
         }
 
+        sb.append(" RequestorUid: ").append(mRequestorUid);
+        sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
+
         sb.append("]");
         return sb.toString();
     }
@@ -1632,6 +1662,7 @@
     private interface NameOf {
         String nameOf(int value);
     }
+
     /**
      * @hide
      */
@@ -1799,4 +1830,120 @@
     private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
         return mPrivateDnsBroken == nc.mPrivateDnsBroken;
     }
+
+    /**
+     * Set the uid of the app making the request.
+     *
+     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
+     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     *
+     * @param uid UID of the app.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull NetworkCapabilities setRequestorUid(int uid) {
+        mRequestorUid = uid;
+        return this;
+    }
+
+    /**
+     * @return the uid of the app making the request.
+     *
+     * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest}
+     * object was not obtained from {@link ConnectivityManager}.
+     * @hide
+     */
+    public int getRequestorUid() {
+        return mRequestorUid;
+    }
+
+    /**
+     * Set the package name of the app making the request.
+     *
+     * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
+     * via the public {@link ConnectivityManager} API's will have this field overwritten.
+     *
+     * @param packageName package name of the app.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) {
+        mRequestorPackageName = packageName;
+        return this;
+    }
+
+    /**
+     * @return the package name of the app making the request.
+     *
+     * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+     * from {@link ConnectivityManager}.
+     * @hide
+     */
+    @Nullable
+    public String getRequestorPackageName() {
+        return mRequestorPackageName;
+    }
+
+    /**
+     * Set the uid and package name of the app making the request.
+     *
+     * Note: This is intended to be only invoked from within connectivitiy service.
+     *
+     * @param uid UID of the app.
+     * @param packageName package name of the app.
+     * @hide
+     */
+    public @NonNull NetworkCapabilities setRequestorUidAndPackageName(
+            int uid, @NonNull String packageName) {
+        return setRequestorUid(uid).setRequestorPackageName(packageName);
+    }
+
+    /**
+     * Test whether the passed NetworkCapabilities satisfies the requestor restrictions of this
+     * capabilities.
+     *
+     * This method is called on the NetworkCapabilities embedded in a request with the
+     * capabilities of an available network. If the available network, sets a specific
+     * requestor (by uid and optionally package name), then this will only match a request from the
+     * same app. If either of the capabilities have an unset uid or package name, then it matches
+     * everything.
+     * <p>
+     * nc is assumed nonnull. Else, NPE.
+     */
+    private boolean satisfiedByRequestor(NetworkCapabilities nc) {
+        // No uid set, matches everything.
+        if (mRequestorUid == Process.INVALID_UID || nc.mRequestorUid == Process.INVALID_UID) {
+            return true;
+        }
+        // uids don't match.
+        if (mRequestorUid != nc.mRequestorUid) return false;
+        // No package names set, matches everything
+        if (null == nc.mRequestorPackageName || null == mRequestorPackageName) return true;
+        // check for package name match.
+        return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+    }
+
+    /**
+     * Combine requestor info of the capabilities.
+     * <p>
+     * This is only legal if either the requestor info of this object is reset, or both info are
+     * equal.
+     * nc is assumed nonnull.
+     */
+    private void combineRequestor(@NonNull NetworkCapabilities nc) {
+        if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
+            throw new IllegalStateException("Can't combine two uids");
+        }
+        if (mRequestorPackageName != null
+                && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
+            throw new IllegalStateException("Can't combine two package names");
+        }
+        setRequestorUid(nc.mRequestorUid);
+        setRequestorPackageName(nc.mRequestorPackageName);
+    }
+
+    private boolean equalsRequestor(NetworkCapabilities nc) {
+        return mRequestorUid == nc.mRequestorUid
+                && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+    }
 }
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 301d203..964f13f 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -380,6 +380,7 @@
         dest.writeInt(requestId);
         dest.writeString(type.name());
     }
+
     public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR =
         new Creator<NetworkRequest>() {
             public NetworkRequest createFromParcel(Parcel in) {
@@ -494,6 +495,31 @@
         return networkCapabilities.getNetworkSpecifier();
     }
 
+    /**
+     * @return the uid of the app making the request.
+     *
+     * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} object was
+     * not obtained from {@link ConnectivityManager}.
+     * @hide
+     */
+    @SystemApi
+    public int getRequestorUid() {
+        return networkCapabilities.getRequestorUid();
+    }
+
+    /**
+     * @return the package name of the app making the request.
+     *
+     * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+     * from {@link ConnectivityManager}.
+     * @hide
+     */
+    @SystemApi
+    @Nullable
+    public String getRequestorPackageName() {
+        return networkCapabilities.getRequestorPackageName();
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index cf31d21..2dd0c4e 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -39,23 +39,6 @@
 
     /**
      * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
-     * check a self-reported UID. A concrete implementation may contain a UID which would be self-
-     * reported by the caller (since NetworkSpecifier implementations should be non-mutable). This
-     * function is called by ConnectivityService and is passed the actual UID of the caller -
-     * allowing the verification of the self-reported UID. In cases of mismatch the implementation
-     * should throw a SecurityException.
-     *
-     * @param requestorUid The UID of the requestor as obtained from its binder.
-     *
-     * @hide
-     */
-    @SystemApi
-    public void assertValidFromUid(int requestorUid) {
-        // empty
-    }
-
-    /**
-     * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
      * perform any redaction of information from the NetworkSpecifier, e.g. if it contains
      * sensitive information. The default implementation simply returns the object itself - i.e.
      * no information is redacted. A concrete implementation may return a modified (copy) of the
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 26d9c7d..7512352 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -27,6 +27,7 @@
 
 import java.io.Closeable;
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.DirectByteBuffer;
 import java.nio.NioUtils;
@@ -272,6 +273,20 @@
         dest.writeFileDescriptor(mFileDescriptor);
     }
 
+    /**
+     * Returns a dup'd ParcelFileDescriptor from the SharedMemory FileDescriptor.
+     * This obeys standard POSIX semantics, where the
+     * new file descriptor shared state such as file position with the
+     * original file descriptor.
+     * TODO: propose this method as a public or system API for next release to achieve parity with
+     *  NDK ASharedMemory_dupFromJava.
+     *
+     * @hide
+     */
+    public ParcelFileDescriptor getFdDup() throws IOException {
+        return ParcelFileDescriptor.dup(mFileDescriptor);
+    }
+
     public static final @android.annotation.NonNull Parcelable.Creator<SharedMemory> CREATOR =
             new Parcelable.Creator<SharedMemory>() {
         @Override
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 25584f1..3508b70 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -101,6 +101,8 @@
     public static final long TRACE_TAG_NNAPI = 1L << 25;
     /** @hide */
     public static final long TRACE_TAG_RRO = 1L << 26;
+    /** @hide */
+    public static final long TRACE_TAG_APEX_MANAGER = 1L << 18;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 6ed1d2c..b202053 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2325,10 +2325,12 @@
      * @param restrictionKey the string key representing the restriction
      * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
      */
+    @TestApi
     @UnsupportedAppUsage
-    @RequiresPermission(Manifest.permission.MANAGE_USERS)
-    public boolean hasBaseUserRestriction(@UserRestrictionKey String restrictionKey,
-            UserHandle userHandle) {
+    @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
+            Manifest.permission.CREATE_USERS})
+    public boolean hasBaseUserRestriction(@UserRestrictionKey @NonNull String restrictionKey,
+            @NonNull UserHandle userHandle) {
         try {
             return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
         } catch (RemoteException re) {
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 21434a2..9d98b3b 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -103,4 +103,9 @@
      * Deletes a storage given its ID. Deletes its bind mounts and unmount it. Stop its data loader.
      */
     void deleteStorage(int storageId);
+
+    /**
+     * Setting up native library directories and extract native libs onto a storage.
+     */
+    boolean configureNativeBinaries(int storageId, in @utf8InCpp String apkFullPath, in @utf8InCpp String libDirRelativePath, in @utf8InCpp String abi);
 }
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 35fa37a..ba38268 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -229,16 +229,41 @@
         if (linkedApkStorage == null) {
             throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent);
         }
-        linkedApkStorage.makeDirectory(afterCodePathName);
-        File[] files = beforeCodeFile.listFiles();
-        for (int i = 0; i < files.length; i++) {
-            if (files[i].isFile()) {
-                String fileName = files[i].getName();
-                apkStorage.makeLink(
-                        fileName, linkedApkStorage, afterCodePathName + "/" + fileName);
+        linkFiles(apkStorage, beforeCodeFile, "", linkedApkStorage, afterCodePathName);
+        apkStorage.unBind(beforeCodePath);
+    }
+
+    /**
+     * Recursively set up directories and link all the files from source storage to target storage.
+     *
+     * @param sourceStorage The storage that has all the files and directories underneath.
+     * @param sourceAbsolutePath The absolute path of the directory that holds all files and dirs.
+     * @param sourceRelativePath The relative path on the source directory, e.g., "" or "lib".
+     * @param targetStorage The target storage that will have the same files and directories.
+     * @param targetRelativePath The relative path to the directory on the target storage that
+     *                           should have all the files and dirs underneath,
+     *                           e.g., "packageName-random".
+     * @throws IOException When makeDirectory or makeLink fails on the Incremental File System.
+     */
+    private void linkFiles(IncrementalStorage sourceStorage, File sourceAbsolutePath,
+            String sourceRelativePath, IncrementalStorage targetStorage,
+            String targetRelativePath) throws IOException {
+        targetStorage.makeDirectory(targetRelativePath);
+        final File[] entryList = sourceAbsolutePath.listFiles();
+        for (int i = 0; i < entryList.length; i++) {
+            final File entry = entryList[i];
+            final String entryName = entryList[i].getName();
+            final String sourceEntryRelativePath =
+                    sourceRelativePath.isEmpty() ? entryName : sourceRelativePath + "/" + entryName;
+            final String targetEntryRelativePath = targetRelativePath + "/" + entryName;
+            if (entry.isFile()) {
+                sourceStorage.makeLink(
+                        sourceEntryRelativePath, targetStorage, targetEntryRelativePath);
+            } else if (entry.isDirectory()) {
+                linkFiles(sourceStorage, entry, sourceEntryRelativePath, targetStorage,
+                        targetEntryRelativePath);
             }
         }
-        apkStorage.unBind(beforeCodePath);
     }
 
     /**
@@ -261,6 +286,13 @@
     }
 
     /**
+     * Checks if Incremental is enabled
+     */
+    public static boolean isEnabled() {
+        return nativeIsEnabled();
+    }
+
+    /**
      * Checks if path is mounted on Incremental File System.
      */
     public static boolean isIncrementalPath(@NonNull String path) {
@@ -268,5 +300,6 @@
     }
 
     /* Native methods */
+    private static native boolean nativeIsEnabled();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
 }
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index c4b843b..5df44ff 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -416,4 +416,24 @@
             return false;
         }
     }
+
+    /**
+     * Configure all the lib files inside Incremental Service, e.g., create lib dirs, create new lib
+     * files, extract original lib file data from zip and then write data to the lib files on the
+     * Incremental File System.
+     *
+     * @param apkFullPath Source APK to extract native libs from.
+     * @param libDirRelativePath Target dir to put lib files, e.g., "lib" or "lib/arm".
+     * @param abi Target ABI of the native lib files. Only extract native libs of this ABI.
+     * @return Success of not.
+     */
+    public boolean configureNativeBinaries(String apkFullPath, String libDirRelativePath,
+            String abi) {
+        try {
+            return mService.configureNativeBinaries(mId, apkFullPath, libDirRelativePath, abi);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 30e4eee08..b9207e5 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1830,6 +1830,17 @@
     public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
 
     /**
+     * Activity Extra: The {@link NotificationChannel#getConversationId()} of the notification
+     * conversation settings to display.
+     * <p>
+     * This is an optional extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}. If
+     * included the system will first look up notification settings by channel and conversation id,
+     * and will fall back to channel id if a specialized channel for this conversation doesn't
+     * exist, similar to {@link NotificationManager#getNotificationChannel(String, String)}.
+     */
+    public static final String EXTRA_CONVERSATION_ID = "android.provider.extra.CONVERSATION_ID";
+
+    /**
      * Activity Action: Show notification redaction settings.
      *
      * @hide
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 707426a..0edd013 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -345,9 +345,11 @@
     }
 
     /**
-     * Notifies the service of {@link SnapshotData snapshot data} associated with a session.
+     * Notifies the service of {@link SnapshotData snapshot data} associated with an activity.
      *
-     * @param sessionId the session's Id
+     * @param sessionId the session's Id. This may also be
+     *                  {@link ContentCaptureSession#NO_SESSION_ID} if no content capture session
+     *                  exists for the activity being snapshotted
      * @param snapshotData the data
      */
     public void onActivitySnapshot(@NonNull ContentCaptureSessionId sessionId,
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/core/java/android/service/notification/ConversationChannelWrapper.aidl
similarity index 76%
rename from core/java/android/os/StatsDimensionsValue.aidl
rename to core/java/android/service/notification/ConversationChannelWrapper.aidl
index 81a14a4..f324158 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/core/java/android/service/notification/ConversationChannelWrapper.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.service.notification;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+parcelable ConversationChannelWrapper;
\ No newline at end of file
diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java
new file mode 100644
index 0000000..9847695
--- /dev/null
+++ b/core/java/android/service/notification/ConversationChannelWrapper.java
@@ -0,0 +1,122 @@
+/**
+ * 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.service.notification;
+
+import android.app.NotificationChannel;
+import android.content.pm.ShortcutInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class ConversationChannelWrapper implements Parcelable {
+
+    private NotificationChannel mNotificationChannel;
+    private CharSequence mGroupLabel;
+    private CharSequence mParentChannelLabel;
+    private ShortcutInfo mShortcutInfo;
+
+    public ConversationChannelWrapper() {}
+
+    protected ConversationChannelWrapper(Parcel in) {
+        mNotificationChannel = in.readParcelable(NotificationChannel.class.getClassLoader());
+        mGroupLabel = in.readCharSequence();
+        mParentChannelLabel = in.readCharSequence();
+        mShortcutInfo = in.readParcelable(ShortcutInfo.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mNotificationChannel, flags);
+        dest.writeCharSequence(mGroupLabel);
+        dest.writeCharSequence(mParentChannelLabel);
+        dest.writeParcelable(mShortcutInfo, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ConversationChannelWrapper> CREATOR =
+            new Creator<ConversationChannelWrapper>() {
+                @Override
+                public ConversationChannelWrapper createFromParcel(Parcel in) {
+                    return new ConversationChannelWrapper(in);
+                }
+
+                @Override
+                public ConversationChannelWrapper[] newArray(int size) {
+                    return new ConversationChannelWrapper[size];
+                }
+            };
+
+
+    public NotificationChannel getNotificationChannel() {
+        return mNotificationChannel;
+    }
+
+    public void setNotificationChannel(
+            NotificationChannel notificationChannel) {
+        mNotificationChannel = notificationChannel;
+    }
+
+    public CharSequence getGroupLabel() {
+        return mGroupLabel;
+    }
+
+    public void setGroupLabel(CharSequence groupLabel) {
+        mGroupLabel = groupLabel;
+    }
+
+    public CharSequence getParentChannelLabel() {
+        return mParentChannelLabel;
+    }
+
+    public void setParentChannelLabel(CharSequence parentChannelLabel) {
+        mParentChannelLabel = parentChannelLabel;
+    }
+
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    public void setShortcutInfo(ShortcutInfo shortcutInfo) {
+        mShortcutInfo = shortcutInfo;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ConversationChannelWrapper that = (ConversationChannelWrapper) o;
+        return Objects.equals(getNotificationChannel(), that.getNotificationChannel()) &&
+                Objects.equals(getGroupLabel(), that.getGroupLabel()) &&
+                Objects.equals(getParentChannelLabel(), that.getParentChannelLabel()) &&
+                Objects.equals(getShortcutInfo(), that.getShortcutInfo());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getNotificationChannel(), getGroupLabel(), getParentChannelLabel(),
+                getShortcutInfo());
+    }
+}
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index d0675ed..b4b5819 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -126,22 +126,22 @@
             = "android.service.quicksettings.ACTIVE_TILE";
 
     /**
-     * Meta-data for a tile to support {@code BooleanState}.
+     * Meta-data for a tile to mark is toggleable.
      * <p>
-     * BooleanState is for tiles that should support switch tile behavior in accessibility. This is
+     * Toggleable tiles support switch tile behavior in accessibility. This is
      * the behavior of most of the framework tiles.
      *
-     * To make a TileService support BooleanState, set this meta-data to true on the TileService's
-     * manifest declaration.
+     * To indicate that a TileService is toggleable, set this meta-data to true on the
+     * TileService's manifest declaration.
      * <pre class="prettyprint">
      * {@literal
-     * <meta-data android:name="android.service.quicksettings.BOOLEAN_TILE"
+     * <meta-data android:name="android.service.quicksettings.TOGGLEABLE_TILE"
      *      android:value="true" />
      * }
      * </pre>
      */
-    public static final String META_DATA_BOOLEAN_TILE =
-            "android.service.quicksettings.BOOLEAN_TILE";
+    public static final String META_DATA_TOGGLEABLE_TILE =
+            "android.service.quicksettings.TOGGLEABLE_TILE";
 
     /**
      * Used to notify SysUI that Listening has be requested.
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 1966f17..a88d389 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -181,14 +181,14 @@
      * Returned by {@link #getSupportedAudioCapabilities()}
      */
     public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION =
-            SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+            SoundTrigger.ModuleProperties.AUDIO_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;
+            SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -249,21 +249,21 @@
         }
 
         /**
-         * The inclusive start of supported range.
+         * Get the beginning of the param range
          *
-         * @return start of range
+         * @return The inclusive start of the supported range.
          */
-        public int start() {
-            return mModelParamRange.start;
+        public int getStart() {
+            return mModelParamRange.getStart();
         }
 
         /**
-         * The inclusive end of supported range.
+         * Get the end of the param range
          *
-         * @return end of range
+         * @return The inclusive end of the supported range.
          */
-        public int end() {
-            return mModelParamRange.end;
+        public int getEnd() {
+            return mModelParamRange.getEnd();
         }
 
         @Override
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 531ade0..e65bd9f 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -412,23 +412,23 @@
      * domain. This indication does not necessarily indicate a change of service state, which should
      * be tracked via {@link #LISTEN_SERVICE_STATE}.
      *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onRegistrationFailed()
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
 
     /**
      * Listen for Barring Information for the current registered / camped cell.
      *
-     * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
-     * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} or
+     * the calling app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
      *
      * @see #onBarringInfoChanged()
      */
-    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
     public static final int LISTEN_BARRING_INFO = 0x80000000;
 
     /*
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 6463509..1d3e6d0 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -66,46 +66,48 @@
     public static PackageParser.SigningDetails verify(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion)
             throws PackageParserException {
+        return verifySignatures(apkPath, minSignatureSchemeVersion, true);
+    }
+
+    /**
+     * Returns the certificates associated with each signer for the given APK without verification.
+     * This method is dangerous and should not be used, unless the caller is absolutely certain the
+     * APK is trusted.
+     *
+     * @throws PackageParserException if there was a problem collecting certificates.
+     */
+    public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
+            String apkPath, int minSignatureSchemeVersion)
+            throws PackageParserException {
+        return verifySignatures(apkPath, minSignatureSchemeVersion, false);
+    }
+
+    /**
+     * Verifies the provided APK using all allowed signing schemas.
+     * @return the certificates associated with each signer.
+     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+     * @throws PackageParserException if there was a problem collecting certificates
+     */
+    private static PackageParser.SigningDetails verifySignatures(String apkPath,
+            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
+            throws PackageParserException {
 
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
             // V3 and before are older than the requested minimum signing version
             throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "No signature found in package of version " + minSignatureSchemeVersion
-            + " or newer for package " + apkPath);
+                            + " or newer for package " + apkPath);
         }
 
         // first try v3
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
         try {
-            ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
-                    ApkSignatureSchemeV3Verifier.verify(apkPath);
-            Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
-            Signature[] signerSigs = convertToSignatures(signerCerts);
-            Signature[] pastSignerSigs = null;
-            if (vSigner.por != null) {
-                // populate proof-of-rotation information
-                pastSignerSigs = new Signature[vSigner.por.certs.size()];
-                for (int i = 0; i < pastSignerSigs.length; i++) {
-                    pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
-                    pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
-                }
-            }
-            return new PackageParser.SigningDetails(
-                    signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                    pastSignerSigs);
+            return verifyV3Signature(apkPath, verifyFull);
         } catch (SignatureNotFoundException e) {
             // not signed with v3, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v3 signature in package " + apkPath, e);
             }
-        } catch (Exception e) {
-            // APK Signature Scheme v2 signature found but did not verify
-            throw new  PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "Failed to collect certificates from " + apkPath
-                            + " using APK Signature Scheme v3", e);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
         // redundant, protective version check
@@ -117,26 +119,14 @@
         }
 
         // try v2
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
         try {
-            Certificate[][] signerCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
-            Signature[] signerSigs = convertToSignatures(signerCerts);
-
-            return new PackageParser.SigningDetails(
-                    signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V2);
+            return verifyV2Signature(apkPath, verifyFull);
         } catch (SignatureNotFoundException e) {
             // not signed with v2, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v2 signature in package " + apkPath, e);
             }
-        } catch (Exception e) {
-            // APK Signature Scheme v2 signature found but did not verify
-            throw new  PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "Failed to collect certificates from " + apkPath
-                            + " using APK Signature Scheme v2", e);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
         // redundant, protective version check
@@ -148,14 +138,83 @@
         }
 
         // v2 didn't work, try jarsigner
-        return verifyV1Signature(apkPath, true);
+        return verifyV1Signature(apkPath, verifyFull);
     }
 
     /**
-     * Verifies the provided APK and returns the certificates associated with each signer.
+     * Verifies the provided APK using V3 schema.
      *
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+     * @return the certificates associated with each signer.
+     * @throws SignatureNotFoundException if there are no V3 signatures in the APK
+     * @throws PackageParserException     if there was a problem collecting certificates
+     */
+    private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
+            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
+        try {
+            ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
+                    verifyFull ? ApkSignatureSchemeV3Verifier.verify(apkPath)
+                            : ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(
+                                    apkPath);
+            Certificate[][] signerCerts = new Certificate[][]{vSigner.certs};
+            Signature[] signerSigs = convertToSignatures(signerCerts);
+            Signature[] pastSignerSigs = null;
+            if (vSigner.por != null) {
+                // populate proof-of-rotation information
+                pastSignerSigs = new Signature[vSigner.por.certs.size()];
+                for (int i = 0; i < pastSignerSigs.length; i++) {
+                    pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+                    pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
+                }
+            }
+            return new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
+        } catch (SignatureNotFoundException e) {
+            throw e;
+        } catch (Exception e) {
+            // APK Signature Scheme v3 signature found but did not verify
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                    "Failed to collect certificates from " + apkPath
+                            + " using APK Signature Scheme v3", e);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    /**
+     * Verifies the provided APK using V2 schema.
      *
+     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+     * @return the certificates associated with each signer.
+     * @throws SignatureNotFoundException if there are no V2 signatures in the APK
+     * @throws PackageParserException     if there was a problem collecting certificates
+     */
+    private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
+            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
+        try {
+            Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath)
+                    : ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
+            Signature[] signerSigs = convertToSignatures(signerCerts);
+            return new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V2);
+        } catch (SignatureNotFoundException e) {
+            throw e;
+        } catch (Exception e) {
+            // APK Signature Scheme v2 signature found but did not verify
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                    "Failed to collect certificates from " + apkPath
+                            + " using APK Signature Scheme v2", e);
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+        }
+    }
+
+    /**
+     * Verifies the provided APK using JAR schema.
+     * @return the certificates associated with each signer.
+     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
      * @throws PackageParserException if there was a problem collecting certificates
      */
     private static PackageParser.SigningDetails verifyV1Signature(
@@ -277,7 +336,7 @@
      *
      * @throws CertificateEncodingException if it is unable to create a Signature object.
      */
-    public static Signature[] convertToSignatures(Certificate[][] certs)
+    private static Signature[] convertToSignatures(Certificate[][] certs)
             throws CertificateEncodingException {
         final Signature[] res = new Signature[certs.length];
         for (int i = 0; i < certs.length; i++) {
@@ -296,99 +355,29 @@
     }
 
     /**
-     * Returns the certificates associated with each signer for the given APK without verification.
-     * This method is dangerous and should not be used, unless the caller is absolutely certain the
-     * APK is trusted.
-     *
-     * @throws PackageParserException if the APK's signature failed to verify.
-     * or greater is not found, except in the case of no JAR signature.
+     * Returns the minimum signature scheme version required for an app targeting the specified
+     * {@code targetSdk}.
      */
-    public static PackageParser.SigningDetails unsafeGetCertsWithoutVerification(
-            String apkPath, int minSignatureSchemeVersion)
-            throws PackageParserException {
-
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V3) {
-            // V3 and before are older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "No signature found in package of version " + minSignatureSchemeVersion
-                            + " or newer for package " + apkPath);
+    public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) {
+        if (targetSdk >= Build.VERSION_CODES.R) {
+            return SignatureSchemeVersion.SIGNING_BLOCK_V2;
         }
+        return SignatureSchemeVersion.JAR;
+    }
 
-        // first try v3
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
-        try {
-            ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
-                    ApkSignatureSchemeV3Verifier.unsafeGetCertsWithoutVerification(apkPath);
-            Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
-            Signature[] signerSigs = convertToSignatures(signerCerts);
-            Signature[] pastSignerSigs = null;
-            if (vSigner.por != null) {
-                // populate proof-of-rotation information
-                pastSignerSigs = new Signature[vSigner.por.certs.size()];
-                for (int i = 0; i < pastSignerSigs.length; i++) {
-                    pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
-                    pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
-                }
-            }
-            return new PackageParser.SigningDetails(
-                    signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                    pastSignerSigs);
-        } catch (SignatureNotFoundException e) {
-            // not signed with v3, try older if allowed
-            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                        "No APK Signature Scheme v3 signature in package " + apkPath, e);
-            }
-        } catch (Exception e) {
-            // APK Signature Scheme v3 signature found but did not verify
-            throw new  PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "Failed to collect certificates from " + apkPath
-                            + " using APK Signature Scheme v3", e);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+    /**
+     * Result of a successful APK verification operation.
+     */
+    public static class Result {
+        public final Certificate[][] certs;
+        public final Signature[] sigs;
+        public final int signatureSchemeVersion;
+
+        public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
+            this.certs = certs;
+            this.sigs = sigs;
+            this.signatureSchemeVersion = signingVersion;
         }
-
-        // redundant, protective version check
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V2) {
-            // V2 and before are older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "No signature found in package of version " + minSignatureSchemeVersion
-                            + " or newer for package " + apkPath);
-        }
-
-        // first try v2
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV2");
-        try {
-            Certificate[][] signerCerts =
-                    ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
-            Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V2);
-        } catch (SignatureNotFoundException e) {
-            // not signed with v2, try older if allowed
-            if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V2) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                        "No APK Signature Scheme v2 signature in package " + apkPath, e);
-            }
-        } catch (Exception e) {
-            // APK Signature Scheme v2 signature found but did not verify
-            throw new  PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "Failed to collect certificates from " + apkPath
-                            + " using APK Signature Scheme v2", e);
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        }
-
-        // redundant, protective version check
-        if (minSignatureSchemeVersion > SignatureSchemeVersion.JAR) {
-            // V1 and is older than the requested minimum signing version
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
-                    "No signature found in package of version " + minSignatureSchemeVersion
-                            + " or newer for package " + apkPath);
-        }
-
-        // v2 didn't work, try jarsigner
-        return verifyV1Signature(apkPath, false);
     }
 
     /**
@@ -416,7 +405,7 @@
      */
     public static byte[] generateApkVerity(String apkPath, ByteBufferFactory bufferFactory)
             throws IOException, SignatureNotFoundException, SecurityException, DigestException,
-                   NoSuchAlgorithmException {
+            NoSuchAlgorithmException {
         // first try v3
         try {
             return ApkSignatureSchemeV3Verifier.generateApkVerity(apkPath, bufferFactory);
@@ -446,30 +435,4 @@
             return null;
         }
     }
-
-    /**
-     * Returns the minimum signature scheme version required for an app targeting the specified
-     * {@code targetSdk}.
-     */
-    public static int getMinimumSignatureSchemeVersionForTargetSdk(int targetSdk) {
-        if (targetSdk >= Build.VERSION_CODES.R) {
-            return SignatureSchemeVersion.SIGNING_BLOCK_V2;
-        }
-        return SignatureSchemeVersion.JAR;
-    }
-
-    /**
-     * Result of a successful APK verification operation.
-     */
-    public static class Result {
-        public final Certificate[][] certs;
-        public final Signature[] sigs;
-        public final int signatureSchemeVersion;
-
-        public Result(Certificate[][] certs, Signature[] sigs, int signingVersion) {
-            this.certs = certs;
-            this.sigs = sigs;
-            this.signatureSchemeVersion = signingVersion;
-        }
-    }
 }
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/core/java/android/view/SurfaceControlViewHost.aidl
similarity index 76%
copy from core/java/android/os/StatsDimensionsValue.aidl
copy to core/java/android/view/SurfaceControlViewHost.aidl
index 81a14a4..3b31ab8 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/core/java/android/view/SurfaceControlViewHost.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018, The Android Open Source Project
+ * Copyright (c) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.view;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+parcelable SurfaceControlViewHost.SurfacePackage;
\ No newline at end of file
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 75d5538..b1c354f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1570,7 +1570,8 @@
     private void reparentSurfacePackage(SurfaceControl.Transaction t,
             SurfaceControlViewHost.SurfacePackage p) {
         // TODO: Link accessibility IDs here.
-        t.reparent(p.getSurfaceControl(), mSurfaceControl);
+        final SurfaceControl sc = p.getSurfaceControl();
+        t.reparent(sc, mSurfaceControl).show(sc);
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a407bd8..a6f8fad 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -12729,12 +12729,14 @@
                 return findViewInsideOutShouldExist(root, mNextFocusForwardId);
             case FOCUS_BACKWARD: {
                 if (mID == View.NO_ID) return null;
-                return root.findViewByPredicateInsideOut(this, new Predicate<View>() {
-                    @Override
-                    public boolean test(View t) {
-                        return t.findViewById(t.mNextFocusForwardId) == View.this;
-                    }
-                });
+                final View rootView = root;
+                final View startView = this;
+                // Since we have forward links but no backward links, we need to find the view that
+                // forward links to this view. We can't just find the view with the specified ID
+                // because view IDs need not be unique throughout the tree.
+                return root.findViewByPredicateInsideOut(startView,
+                    t -> findViewInsideOutShouldExist(rootView, t, t.mNextFocusForwardId)
+                            == startView);
             }
         }
         return null;
@@ -12764,11 +12766,15 @@
     }
 
     private View findViewInsideOutShouldExist(View root, int id) {
+        return findViewInsideOutShouldExist(root, this, id);
+    }
+
+    private View findViewInsideOutShouldExist(View root, View start, int id) {
         if (mMatchIdPredicate == null) {
             mMatchIdPredicate = new MatchIdPredicate();
         }
         mMatchIdPredicate.mId = id;
-        View result = root.findViewByPredicateInsideOut(this, mMatchIdPredicate);
+        View result = root.findViewByPredicateInsideOut(start, mMatchIdPredicate);
         if (result == null) {
             Log.w(VIEW_LOG_TAG, "couldn't find view with id " + id);
         }
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index 33f21f2..cf34b0b 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -25,6 +27,8 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -36,10 +40,14 @@
 public class WindowContainerTransaction implements Parcelable {
     private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
 
+    // Flat list because re-order operations are order-dependent
+    private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
+
     public WindowContainerTransaction() {}
 
     protected WindowContainerTransaction(Parcel in) {
         in.readMap(mChanges, null /* loader */);
+        in.readList(mHierarchyOps, null /* loader */);
     }
 
     private Change getOrCreateChange(IBinder token) {
@@ -97,10 +105,39 @@
         return this;
     }
 
+    /**
+     * Reparents a container into another one. The effect of a {@code null} parent can vary. For
+     * example, reparenting a stack to {@code null} will reparent it to its display.
+     *
+     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+     *              the bottom.
+     */
+    public WindowContainerTransaction reparent(@NonNull IWindowContainer child,
+            @Nullable IWindowContainer parent, boolean onTop) {
+        mHierarchyOps.add(new HierarchyOp(child.asBinder(),
+                parent == null ? null : parent.asBinder(), onTop));
+        return this;
+    }
+
+    /**
+     * Reorders a container within its parent.
+     *
+     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
+     *              the bottom.
+     */
+    public WindowContainerTransaction reorder(@NonNull IWindowContainer child, boolean onTop) {
+        mHierarchyOps.add(new HierarchyOp(child.asBinder(), onTop));
+        return this;
+    }
+
     public Map<IBinder, Change> getChanges() {
         return mChanges;
     }
 
+    public List<HierarchyOp> getHierarchyOps() {
+        return mHierarchyOps;
+    }
+
     @Override
     public String toString() {
         return "WindowContainerTransaction { changes = " + mChanges + " }";
@@ -109,6 +146,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeMap(mChanges);
+        dest.writeList(mHierarchyOps);
     }
 
     @Override
@@ -249,4 +287,88 @@
             }
         };
     }
+
+    /**
+     * Holds information about a reparent/reorder operation in the hierarchy. This is separate from
+     * Changes because they must be executed in the same order that they are added.
+     */
+    public static class HierarchyOp implements Parcelable {
+        private final IBinder mContainer;
+
+        // If this is same as mContainer, then only change position, don't reparent.
+        private final IBinder mReparent;
+
+        // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
+        private final boolean mToTop;
+
+        public HierarchyOp(@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
+            mContainer = container;
+            mReparent = reparent;
+            mToTop = toTop;
+        }
+
+        public HierarchyOp(@NonNull IBinder container, boolean toTop) {
+            mContainer = container;
+            mReparent = container;
+            mToTop = toTop;
+        }
+
+        protected HierarchyOp(Parcel in) {
+            mContainer = in.readStrongBinder();
+            mReparent = in.readStrongBinder();
+            mToTop = in.readBoolean();
+        }
+
+        public boolean isReparent() {
+            return mContainer != mReparent;
+        }
+
+        @Nullable
+        public IBinder getNewParent() {
+            return mReparent;
+        }
+
+        @NonNull
+        public IBinder getContainer() {
+            return mContainer;
+        }
+
+        public boolean getToTop() {
+            return mToTop;
+        }
+
+        @Override
+        public String toString() {
+            if (isReparent()) {
+                return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
+                        + mReparent + "}";
+            } else {
+                return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+            }
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeStrongBinder(mContainer);
+            dest.writeStrongBinder(mReparent);
+            dest.writeBoolean(mToTop);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
+            @Override
+            public HierarchyOp createFromParcel(Parcel in) {
+                return new HierarchyOp(in);
+            }
+
+            @Override
+            public HierarchyOp[] newArray(int size) {
+                return new HierarchyOp[size];
+            }
+        };
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 05cf3ed..bf2de14 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -4854,8 +4854,11 @@
                 new AccessibilityAction(R.id.accessibilityActionPressAndHold);
 
         /**
-         * Action to send ime action. A node should expose this action only for views that are
-         * currently with input focus and editable.
+         * Action to send an ime action which is from
+         * {@link android.view.inputmethod.EditorInfo#actionId}. This action would be
+         * {@link android.view.inputmethod.EditorInfo#IME_ACTION_UNSPECIFIED} if no specific
+         * actionId defined. A node should expose this action only for views that are currently
+         * with input focus and editable.
          */
         @NonNull public static final AccessibilityAction ACTION_IME_ENTER =
                 new AccessibilityAction(R.id.accessibilityActionImeEnter);
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 232d96b..2134dab 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -22,6 +22,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.util.DebugUtils;
 import android.util.Log;
 import android.view.View;
@@ -50,7 +51,11 @@
 
     private static final Random sIdGenerator = new Random();
 
-    /** @hide */
+    /**
+    *  ID used to indicate that a session does not exist
+    *  @hide
+    */
+    @SystemApi
     public static final int NO_SESSION_ID = 0;
 
     /**
diff --git a/core/java/android/view/inputmethod/InlineSuggestionInfo.java b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
index 703b64f..024de4d 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionInfo.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionInfo.java
@@ -80,7 +80,6 @@
             @NonNull InlinePresentationSpec presentationSpec,
             @NonNull @Source String source,
             @Nullable String[] autofillHints) {
-        // TODO(b/147394280): Add CTS test for the type field.
         return new InlineSuggestionInfo(presentationSpec, source, autofillHints, TYPE_SUGGESTION);
     }
 
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f0c16aa..dbab81b1 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -664,8 +664,6 @@
          */
         @Override
         public void setCurrentRootView(ViewRootImpl rootView) {
-            // If the mCurRootView is losing window focus, release the strong reference to it
-            // so as not to prevent it from being garbage-collected.
             if (mWindowFocusGainFuture != null) {
                 mWindowFocusGainFuture.cancel(false /* mayInterruptIfRunning */);
                 mWindowFocusGainFuture = null;
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index a6c83a1..dfbec9b 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -25,7 +25,6 @@
 import android.os.ServiceManager;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfig.Properties;
-import android.util.SparseArray;
 import android.view.textclassifier.TextClassifier.TextClassifierType;
 
 import com.android.internal.annotations.GuardedBy;
@@ -62,8 +61,6 @@
     @Nullable
     private TextClassifier mLocalTextClassifier;
     @GuardedBy("mLock")
-    private SparseArray<TextClassifier> mSystemTextClassifiers = new SparseArray<>();
-    @GuardedBy("mLock")
     private TextClassificationSessionFactory mSessionFactory;
     @GuardedBy("mLock")
     private TextClassificationConstants mSettings;
@@ -207,23 +204,17 @@
     /** @hide */
     private TextClassifier getSystemTextClassifier(@TextClassifierType int type) {
         synchronized (mLock) {
-            if (mSystemTextClassifiers.get(type) == null
-                    && getSettings().isSystemTextClassifierEnabled()) {
+            if (getSettings().isSystemTextClassifierEnabled()) {
                 try {
-                    mSystemTextClassifiers.put(
-                            type,
-                            new SystemTextClassifier(
-                                    mContext,
-                                    getSettings(),
-                                    /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE));
-                    Log.d(LOG_TAG, "Initialized SystemTextClassifier, type = " + type);
+                    Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = " + type);
+                    return new SystemTextClassifier(
+                            mContext,
+                            getSettings(),
+                            /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE);
                 } catch (ServiceManager.ServiceNotFoundException e) {
                     Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e);
                 }
             }
-            if (mSystemTextClassifiers.get(type) != null) {
-                return mSystemTextClassifiers.get(type);
-            }
             return TextClassifier.NO_OP;
         }
     }
@@ -263,7 +254,6 @@
     private void invalidateTextClassifiers() {
         synchronized (mLock) {
             mLocalTextClassifier = null;
-            mSystemTextClassifiers.clear();
         }
     }
 
diff --git a/core/java/android/webkit/WebResourceRequest.java b/core/java/android/webkit/WebResourceRequest.java
index 964b6f8..0b307e6 100644
--- a/core/java/android/webkit/WebResourceRequest.java
+++ b/core/java/android/webkit/WebResourceRequest.java
@@ -32,10 +32,10 @@
     Uri getUrl();
 
     /**
-     * Gets whether the request was made for the main frame.
+     * Gets whether the request was made in order to fetch the main frame's document.
      *
-     * @return whether the request was made for the main frame. Will be {@code false} for iframes,
-     *         for example.
+     * @return whether the request was made for the main frame document. Will be
+     *         {@code false} for subresources or iframes, for example.
      */
     boolean isForMainFrame();
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cbfa05c..469ab2e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -422,9 +422,6 @@
      */
     static final int PROCESS_TEXT_REQUEST_CODE = 100;
 
-    // Accessibility action to send IME custom action for CTS testing.
-    public static final int ACCESSIBILITY_ACTION_IME_ENTER = R.id.accessibilityActionImeEnter;
-
     /**
      *  Return code of {@link #doKeyDown}.
      */
@@ -11758,7 +11755,7 @@
                 }
                 AccessibilityNodeInfo.AccessibilityAction action =
                         new AccessibilityNodeInfo.AccessibilityAction(
-                                ACCESSIBILITY_ACTION_IME_ENTER, imeActionLabel);
+                                R.id.accessibilityActionImeEnter, imeActionLabel);
                 info.addAction(action);
             }
         }
@@ -12072,7 +12069,7 @@
                     }
                 }
             } return true;
-            case ACCESSIBILITY_ACTION_IME_ENTER: {
+            case R.id.accessibilityActionImeEnter: {
                 if (isFocused() && isTextEditable()) {
                     final int imeActionId = (arguments != null) ? arguments.getInt(
                             AccessibilityNodeInfo.ACTION_ARGUMENT_IME_ACTION_ID_INT,
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index a2c70b9..8c52d1f 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -263,9 +263,14 @@
     /**
      * Return the view.
      *
-     * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps
-     * targeting API level {@link Build.VERSION_CODES#R} or higher that haven't called {@link
-     * #setView(View)} with a non-{@code null} view, this method will return {@code null}.
+     * <p>Toasts constructed with {@link #Toast(Context)} that haven't called {@link #setView(View)}
+     * with a non-{@code null} view will return {@code null} here.
+     *
+     * <p>Starting from Android {@link Build.VERSION_CODES#R}, in apps targeting API level {@link
+     * Build.VERSION_CODES#R} or higher, toasts constructed with {@link #makeText(Context,
+     * CharSequence, int)} or its variants will also return {@code null} here unless they had called
+     * {@link #setView(View)} with a non-{@code null} view. If you want to be notified when the
+     * toast is shown or hidden, use {@link #addCallback(Callback)}.
      *
      * @see #setView
      * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the
@@ -276,7 +281,7 @@
      *      will not have custom toast views displayed.
      */
     @Deprecated
-    public View getView() {
+    @Nullable public View getView() {
         return mNextView;
     }
 
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 82eb55a..5f35622 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -471,6 +471,7 @@
             holder.mSwitchItem.setVisibility(View.GONE);
             holder.mItemContainer.setVisibility(isLaunchMenuMode ? View.GONE : View.VISIBLE);
             holder.mItemView.setEnabled(enabledState);
+            holder.mItemView.setClickable(!enabledState);
         }
 
         private void updateInvisibleActionItemVisibility(@NonNull Context context,
@@ -485,6 +486,7 @@
             holder.mItemContainer.setVisibility((mShortcutMenuMode == ShortcutMenuMode.EDIT)
                     ? View.VISIBLE : View.GONE);
             holder.mItemView.setEnabled(true);
+            holder.mItemView.setClickable(false);
         }
 
         private void updateIntuitiveActionItemVisibility(@NonNull Context context,
@@ -504,6 +506,7 @@
             holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled);
             holder.mItemContainer.setVisibility(View.VISIBLE);
             holder.mItemView.setEnabled(true);
+            holder.mItemView.setClickable(false);
         }
 
         private void updateBounceActionItemVisibility(@NonNull Context context,
@@ -518,6 +521,7 @@
             holder.mSwitchItem.setVisibility(View.GONE);
             holder.mItemContainer.setVisibility(View.VISIBLE);
             holder.mItemView.setEnabled(true);
+            holder.mItemView.setClickable(false);
         }
     }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index bbb7513..a934de3 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1487,7 +1487,6 @@
         for (int i = 0; i < tabWidget.getChildCount(); i++) {
             TextView title = tabWidget.getChildAt(i).findViewById(android.R.id.title);
             title.setTextColor(getColor(R.color.resolver_tabs_inactive_color));
-            title.setAllCaps(false);
         }
     }
 
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 22fe31e..0ccc45e 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -25,6 +25,7 @@
 import static android.system.OsConstants.S_IXGRP;
 import static android.system.OsConstants.S_IXOTH;
 
+import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -32,7 +33,12 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.SELinux;
+import android.os.ServiceManager;
+import android.os.incremental.IIncrementalService;
+import android.os.incremental.IncrementalManager;
+import android.os.incremental.IncrementalStorage;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Slog;
@@ -44,6 +50,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
+import java.nio.file.Path;
 import java.util.List;
 
 /**
@@ -73,6 +80,7 @@
         private final CloseGuard mGuard = CloseGuard.get();
         private volatile boolean mClosed;
 
+        final String[] apkPaths;
         final long[] apkHandles;
         final boolean multiArch;
         final boolean extractNativeLibs;
@@ -103,9 +111,11 @@
         private static Handle create(List<String> codePaths, boolean multiArch,
                 boolean extractNativeLibs, boolean debuggable) throws IOException {
             final int size = codePaths.size();
+            final String[] apkPaths = new String[size];
             final long[] apkHandles = new long[size];
             for (int i = 0; i < size; i++) {
                 final String path = codePaths.get(i);
+                apkPaths[i] = path;
                 apkHandles[i] = nativeOpenApk(path);
                 if (apkHandles[i] == 0) {
                     // Unwind everything we've opened so far
@@ -116,7 +126,7 @@
                 }
             }
 
-            return new Handle(apkHandles, multiArch, extractNativeLibs, debuggable);
+            return new Handle(apkPaths, apkHandles, multiArch, extractNativeLibs, debuggable);
         }
 
         public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
@@ -127,11 +137,13 @@
                 throw new IOException("Unable to open APK " + path + " from fd " + fd);
             }
 
-            return new Handle(apkHandles, lite.multiArch, lite.extractNativeLibs, lite.debuggable);
+            return new Handle(new String[]{path}, apkHandles, lite.multiArch,
+                    lite.extractNativeLibs, lite.debuggable);
         }
 
-        Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs,
-                boolean debuggable) {
+        Handle(String[] apkPaths, long[] apkHandles, boolean multiArch,
+                boolean extractNativeLibs, boolean debuggable) {
+            this.apkPaths = apkPaths;
             this.apkHandles = apkHandles;
             this.multiArch = multiArch;
             this.extractNativeLibs = extractNativeLibs;
@@ -313,40 +325,58 @@
     }
 
     public static int copyNativeBinariesForSupportedAbi(Handle handle, File libraryRoot,
-            String[] abiList, boolean useIsaSubdir) throws IOException {
-        createNativeLibrarySubdir(libraryRoot);
-
+            String[] abiList, boolean useIsaSubdir, boolean isIncremental) throws IOException {
         /*
          * If this is an internal application or our nativeLibraryPath points to
          * the app-lib directory, unpack the libraries if necessary.
          */
         int abi = findSupportedAbi(handle, abiList);
-        if (abi >= 0) {
-            /*
-             * If we have a matching instruction set, construct a subdir under the native
-             * library root that corresponds to this instruction set.
-             */
-            final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
-            final File subDir;
-            if (useIsaSubdir) {
-                final File isaSubdir = new File(libraryRoot, instructionSet);
-                createNativeLibrarySubdir(isaSubdir);
-                subDir = isaSubdir;
-            } else {
-                subDir = libraryRoot;
-            }
+        if (abi < 0) {
+            return abi;
+        }
 
-            int copyRet = copyNativeBinaries(handle, subDir, abiList[abi]);
-            if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
-                return copyRet;
+        /*
+         * If we have a matching instruction set, construct a subdir under the native
+         * library root that corresponds to this instruction set.
+         */
+        final String supportedAbi = abiList[abi];
+        final String instructionSet = VMRuntime.getInstructionSet(supportedAbi);
+        final File subDir;
+        if (useIsaSubdir) {
+            subDir = new File(libraryRoot, instructionSet);
+        } else {
+            subDir = libraryRoot;
+        }
+
+        if (isIncremental) {
+            int res =
+                    incrementalConfigureNativeBinariesForSupportedAbi(handle, subDir, supportedAbi);
+            if (res != PackageManager.INSTALL_SUCCEEDED) {
+                // TODO(b/133435829): the caller of this function expects that we return the index
+                // to the supported ABI. However, any non-negative integer can be a valid index.
+                // We should fix this function and make sure it doesn't accidentally return an error
+                // code that can also be a valid index.
+                return res;
             }
+            return abi;
+        }
+
+        // For non-incremental, use regular extraction and copy
+        createNativeLibrarySubdir(libraryRoot);
+        if (subDir != libraryRoot) {
+            createNativeLibrarySubdir(subDir);
+        }
+
+        int copyRet = copyNativeBinaries(handle, subDir, supportedAbi);
+        if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
+            return copyRet;
         }
 
         return abi;
     }
 
     public static int copyNativeBinariesWithOverride(Handle handle, File libraryRoot,
-            String abiOverride) {
+            String abiOverride, boolean isIncremental) {
         try {
             if (handle.multiArch) {
                 // Warn if we've set an abiOverride for multi-lib packages..
@@ -359,7 +389,8 @@
                 int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
-                            Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
+                            Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */,
+                            isIncremental);
                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
                         Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
@@ -369,7 +400,8 @@
 
                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                     copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot,
-                            Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
+                            Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */,
+                            isIncremental);
                     if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
                             copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
                         Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
@@ -392,7 +424,7 @@
                 }
 
                 int copyRet = copyNativeBinariesForSupportedAbi(handle, libraryRoot, abiList,
-                        true /* use isa specific subdirs */);
+                        true /* use isa specific subdirs */, isIncremental);
                 if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                     Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
                     return copyRet;
@@ -449,29 +481,61 @@
      * Service will create native library directories and set up native library binary files in the
      * same structure as they are in non-incremental installations.
      *
-     * @param pkg The package to be installed, including all the APK files.
-     * @param handle The pointer to an zip archive.
-     * @param libraryRoot The root directory of the native library files, e.g., lib/
-     * @param abiList The list of ABIs that are supported by the current device.
-     * @param useIsaSubdir Whether or not to set up a sub dir for the ISA.
-     * @return ABI code if installation succeeds or error code if installation fails.
+     * @param handle The Handle object that contains all apk paths.
+     * @param libSubDir The target directory to put the native library files, e.g., lib/ or lib/arm
+     * @param abi The abi that is supported by the current device.
+     * @return Integer code if installation succeeds or fails.
      */
-    public static int configureNativeBinariesForSupportedAbi(AndroidPackage pkg, Handle handle,
-            File libraryRoot, String[] abiList, boolean useIsaSubdir) {
-        int abi = findSupportedAbi(handle, abiList);
-        if (abi < 0) {
-            Slog.e(TAG, "Failed to find find matching ABI.");
-            return abi;
+    private static int incrementalConfigureNativeBinariesForSupportedAbi(Handle handle,
+            File libSubDir, String abi) {
+        final String[] apkPaths = handle.apkPaths;
+        if (apkPaths == null || apkPaths.length == 0) {
+            Slog.e(TAG, "No apks to extract native libraries from.");
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
         }
 
-        // Currently only support installations that have pre-configured native library files
-        // TODO(b/136132412): implement this after incfs supports file mapping
-        if (!libraryRoot.exists()) {
-            Slog.e(TAG, "Incremental installation currently does not configure native libs");
-            return INSTALL_FAILED_NO_MATCHING_ABIS;
+        final IBinder incrementalService = ServiceManager.getService(Context.INCREMENTAL_SERVICE);
+        if (incrementalService == null) {
+            //TODO(b/133435829): add incremental specific error codes
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        }
+        final IncrementalManager incrementalManager = new IncrementalManager(
+                IIncrementalService.Stub.asInterface(incrementalService));
+        final File apkParent = new File(apkPaths[0]).getParentFile();
+        IncrementalStorage incrementalStorage =
+                incrementalManager.openStorage(apkParent.getAbsolutePath());
+        if (incrementalStorage == null) {
+            Slog.e(TAG, "Failed to find incremental storage");
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
         }
 
-        return abi;
+        String libRelativeDir = getRelativePath(apkParent, libSubDir);
+        if (libRelativeDir == null) {
+            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+        }
+
+        for (int i = 0; i < apkPaths.length; i++) {
+            if (!incrementalStorage.configureNativeBinaries(apkPaths[i], libRelativeDir, abi)) {
+                return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
+            }
+        }
+        return PackageManager.INSTALL_SUCCEEDED;
+    }
+
+    private static String getRelativePath(File base, File target) {
+        try {
+            final Path basePath = base.toPath();
+            final Path targetPath = target.toPath();
+            final Path relativePath = basePath.relativize(targetPath);
+            if (relativePath.toString().isEmpty()) {
+                return "";
+            }
+            return relativePath.toString();
+        } catch (IllegalArgumentException ex) {
+            Slog.e(TAG, "Failed to find relative path between: " + base.getAbsolutePath()
+                    + " and: " + target.getAbsolutePath());
+            return null;
+        }
     }
 
     // We don't care about the other return values for now.
diff --git a/core/java/com/android/internal/policy/TaskResizingAlgorithm.java b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
new file mode 100644
index 0000000..1fce098
--- /dev/null
+++ b/core/java/com/android/internal/policy/TaskResizingAlgorithm.java
@@ -0,0 +1,179 @@
+/*
+ * 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.policy;
+
+import android.annotation.IntDef;
+import android.graphics.Point;
+import android.graphics.Rect;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Given a move coordinate (x, y), the original taks bounds and relevant details, calculate the new
+ * bounds.
+ *
+ * @hide
+ */
+public class TaskResizingAlgorithm {
+
+    @IntDef(flag = true,
+            value = {
+                    CTRL_NONE,
+                    CTRL_LEFT,
+                    CTRL_RIGHT,
+                    CTRL_TOP,
+                    CTRL_BOTTOM
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CtrlType {}
+
+    public static final int CTRL_NONE   = 0x0;
+    public static final int CTRL_LEFT   = 0x1;
+    public static final int CTRL_RIGHT  = 0x2;
+    public static final int CTRL_TOP    = 0x4;
+    public static final int CTRL_BOTTOM = 0x8;
+
+    // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
+    // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
+    // aspect he desires.
+    @VisibleForTesting
+    public static final float MIN_ASPECT = 1.2f;
+
+    /**
+     * Given a (x, y) point and its original starting down point and its original bounds, calculate
+     * and return a new resized bound.
+     * @param x the new moved X point.
+     * @param y the new moved Y point.
+     * @param startDragX the original starting X point.
+     * @param startDragY the original starting Y point.
+     * @param originalBounds the original bound before resize.
+     * @param ctrlType The type of resize operation.
+     * @param minVisibleWidth The minimal width required for the new size.
+     * @param minVisibleHeight The minimal height required for the new size.
+     * @param maxVisibleSize The maximum size allowed.
+     * @param preserveOrientation
+     * @param startOrientationWasLandscape
+     * @return
+     */
+    public static Rect resizeDrag(float x, float y, float startDragX, float startDragY,
+            Rect originalBounds, int ctrlType, int minVisibleWidth, int minVisibleHeight,
+            Point maxVisibleSize, boolean preserveOrientation,
+            boolean startOrientationWasLandscape) {
+        // This is a resizing operation.
+        // We need to keep various constraints:
+        // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
+        // 2. The orientation is kept - if required.
+        final int deltaX = Math.round(x - startDragX);
+        final int deltaY = Math.round(y - startDragY);
+        int left = originalBounds.left;
+        int top = originalBounds.top;
+        int right = originalBounds.right;
+        int bottom = originalBounds.bottom;
+
+        // Calculate the resulting width and height of the drag operation.
+        int width = right - left;
+        int height = bottom - top;
+        if ((ctrlType & CTRL_LEFT) != 0) {
+            width = Math.max(minVisibleWidth, width - deltaX);
+        } else if ((ctrlType & CTRL_RIGHT) != 0) {
+            width = Math.max(minVisibleWidth, width + deltaX);
+        }
+        if ((ctrlType & CTRL_TOP) != 0) {
+            height = Math.max(minVisibleHeight, height - deltaY);
+        } else if ((ctrlType & CTRL_BOTTOM) != 0) {
+            height = Math.max(minVisibleHeight, height + deltaY);
+        }
+
+        // If we have to preserve the orientation - check that we are doing so.
+        final float aspect = (float) width / (float) height;
+        if (preserveOrientation && ((startOrientationWasLandscape && aspect < MIN_ASPECT)
+                || (!startOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
+            // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
+            // drag axis. What ever is producing the bigger rectangle will be chosen.
+            int width1;
+            int width2;
+            int height1;
+            int height2;
+            if (startOrientationWasLandscape) {
+                // Assuming that the width is our target we calculate the height.
+                width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
+                height1 = Math.min(height, Math.round((float) width1 / MIN_ASPECT));
+                if (height1 < minVisibleHeight) {
+                    // If the resulting height is too small we adjust to the minimal size.
+                    height1 = minVisibleHeight;
+                    width1 = Math.max(minVisibleWidth,
+                            Math.min(maxVisibleSize.x, Math.round((float) height1 * MIN_ASPECT)));
+                }
+                // Assuming that the height is our target we calculate the width.
+                height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
+                width2 = Math.max(width, Math.round((float) height2 * MIN_ASPECT));
+                if (width2 < minVisibleWidth) {
+                    // If the resulting width is too small we adjust to the minimal size.
+                    width2 = minVisibleWidth;
+                    height2 = Math.max(minVisibleHeight,
+                            Math.min(maxVisibleSize.y, Math.round((float) width2 / MIN_ASPECT)));
+                }
+            } else {
+                // Assuming that the width is our target we calculate the height.
+                width1 = Math.max(minVisibleWidth, Math.min(maxVisibleSize.x, width));
+                height1 = Math.max(height, Math.round((float) width1 * MIN_ASPECT));
+                if (height1 < minVisibleHeight) {
+                    // If the resulting height is too small we adjust to the minimal size.
+                    height1 = minVisibleHeight;
+                    width1 = Math.max(minVisibleWidth,
+                            Math.min(maxVisibleSize.x, Math.round((float) height1 / MIN_ASPECT)));
+                }
+                // Assuming that the height is our target we calculate the width.
+                height2 = Math.max(minVisibleHeight, Math.min(maxVisibleSize.y, height));
+                width2 = Math.min(width, Math.round((float) height2 / MIN_ASPECT));
+                if (width2 < minVisibleWidth) {
+                    // If the resulting width is too small we adjust to the minimal size.
+                    width2 = minVisibleWidth;
+                    height2 = Math.max(minVisibleHeight,
+                            Math.min(maxVisibleSize.y, Math.round((float) width2 * MIN_ASPECT)));
+                }
+            }
+
+            // Use the bigger of the two rectangles if the major change was positive, otherwise
+            // do the opposite.
+            final boolean grows = width > (right - left) || height > (bottom - top);
+            if (grows == (width1 * height1 > width2 * height2)) {
+                width = width1;
+                height = height1;
+            } else {
+                width = width2;
+                height = height2;
+            }
+        }
+
+        // Generate the final bounds by keeping the opposite drag edge constant.
+        if ((ctrlType & CTRL_LEFT) != 0) {
+            left = right - width;
+        } else { // Note: The right might have changed - if we pulled at the right or not.
+            right = left + width;
+        }
+        if ((ctrlType & CTRL_TOP) != 0) {
+            top = bottom - height;
+        } else { // Note: The height might have changed - if we pulled at the bottom or not.
+            bottom = top + height;
+        }
+        return new Rect(left, top, right, bottom);
+    }
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 74b481c..2fcc1de 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -29,6 +29,7 @@
 import android.os.Process;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.os.incremental.IncrementalManager;
 import android.os.storage.StorageManager;
 import android.permission.PermissionManager.SplitPermissionInfo;
 import android.text.TextUtils;
@@ -902,7 +903,6 @@
                     } break;
                     case "component-override": {
                         readComponentOverrides(parser, permFile);
-                        XmlUtils.skipCurrentTag(parser);
                     } break;
                     case "backup-transport-whitelisted-service": {
                         if (allowFeatures) {
@@ -1156,6 +1156,10 @@
             addFeature(PackageManager.FEATURE_RAM_NORMAL, 0);
         }
 
+        if (IncrementalManager.isEnabled()) {
+            addFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY, 0);
+        }
+
         for (String featureName : mUnavailableFeatures) {
             removeFeature(featureName);
         }
@@ -1398,8 +1402,7 @@
 
         final int depth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, depth)) {
-            String name = parser.getName();
-            if ("component".equals(name)) {
+            if ("component".equals(parser.getName())) {
                 String clsname = parser.getAttributeValue(null, "class");
                 String enabled = parser.getAttributeValue(null, "enabled");
                 if (clsname == null) {
@@ -1427,8 +1430,6 @@
                 }
 
                 componentEnabledStates.put(clsname, !"false".equals(enabled));
-            } else {
-                XmlUtils.skipCurrentTag(parser);
             }
         }
     }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 35eb0fc..76e7e19 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -153,7 +153,7 @@
                 "android_util_MemoryIntArray.cpp",
                 "android_util_Process.cpp",
                 "android_util_jar_StrictJarFile.cpp",
-                "android_media_AudioDeviceAddress.cpp",
+                "android_media_AudioDevice.cpp",
                 "android_media_AudioEffectDescriptor.cpp",
                 "android_media_AudioRecord.cpp",
                 "android_media_AudioSystem.cpp",
@@ -182,6 +182,7 @@
                 "android_hardware_UsbRequest.cpp",
                 "android_hardware_location_ActivityRecognitionHardware.cpp",
                 "android_util_FileObserver.cpp",
+                "android/graphics/GraphicsStatsService.cpp",
                 "android/graphics/SurfaceTexture.cpp",
                 "android/opengl/poly_clip.cpp", // TODO: .arm
                 "android/opengl/util.cpp",
@@ -273,6 +274,7 @@
                 "libstats_jni",
                 "libstatslog",
                 "server_configurable_flags",
+                "libstatspull",
             ],
             export_shared_lib_headers: [
                 // AndroidRuntime.h depends on nativehelper/jni.h
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 657336e..d0e8fd3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -86,7 +86,7 @@
 extern int register_android_hardware_UsbRequest(JNIEnv *env);
 extern int register_android_hardware_location_ActivityRecognitionHardware(JNIEnv* env);
 
-extern int register_android_media_AudioDeviceAddress(JNIEnv *env);
+extern int register_android_media_AudioDevice(JNIEnv* env);
 extern int register_android_media_AudioEffectDescriptor(JNIEnv *env);
 extern int register_android_media_AudioRecord(JNIEnv *env);
 extern int register_android_media_AudioSystem(JNIEnv *env);
@@ -631,6 +631,8 @@
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
     char foregroundHeapGrowthMultiplierOptsBuf[
             sizeof("-XX:ForegroundHeapGrowthMultiplier=")-1 + PROPERTY_VALUE_MAX];
+    char finalizerTimeoutMsOptsBuf[sizeof("-XX:FinalizerTimeoutMs=")-1 + PROPERTY_VALUE_MAX];
+    char threadSuspendTimeoutOptsBuf[sizeof("-XX:ThreadSuspendTimeout=")-1 + PROPERTY_VALUE_MAX];
     char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
@@ -784,7 +786,15 @@
     parseRuntimeOption("dalvik.vm.foreground-heap-growth-multiplier",
                        foregroundHeapGrowthMultiplierOptsBuf,
                        "-XX:ForegroundHeapGrowthMultiplier=");
-
+    /*
+     * Finalizer and thread suspend timeouts.
+     */
+    parseRuntimeOption("dalvik.vm.finalizer-timeout-ms",
+                       finalizerTimeoutMsOptsBuf,
+                       "-XX:FinalizerTimeoutMs=");
+    parseRuntimeOption("dalvik.vm.thread-suspend-timeout-ms",
+                       threadSuspendTimeoutOptsBuf,
+                       "-XX:ThreadSuspendTimeout=");
     /*
      * JIT related options.
      */
@@ -1424,140 +1434,140 @@
 }
 
 static const RegJNIRec gRegJNI[] = {
-    REG_JNI(register_com_android_internal_os_RuntimeInit),
-    REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
-    REG_JNI(register_android_os_SystemClock),
-    REG_JNI(register_android_util_EventLog),
-    REG_JNI(register_android_util_Log),
-    REG_JNI(register_android_util_MemoryIntArray),
-    REG_JNI(register_android_util_StatsLog),
-    REG_JNI(register_android_util_StatsLogInternal),
-    REG_JNI(register_android_app_admin_SecurityLog),
-    REG_JNI(register_android_content_AssetManager),
-    REG_JNI(register_android_content_StringBlock),
-    REG_JNI(register_android_content_XmlBlock),
-    REG_JNI(register_android_content_res_ApkAssets),
-    REG_JNI(register_android_text_AndroidCharacter),
-    REG_JNI(register_android_text_Hyphenator),
-    REG_JNI(register_android_view_InputDevice),
-    REG_JNI(register_android_view_KeyCharacterMap),
-    REG_JNI(register_android_os_Process),
-    REG_JNI(register_android_os_SystemProperties),
-    REG_JNI(register_android_os_Binder),
-    REG_JNI(register_android_os_Parcel),
-    REG_JNI(register_android_os_HidlMemory),
-    REG_JNI(register_android_os_HidlSupport),
-    REG_JNI(register_android_os_HwBinder),
-    REG_JNI(register_android_os_HwBlob),
-    REG_JNI(register_android_os_HwParcel),
-    REG_JNI(register_android_os_HwRemoteBinder),
-    REG_JNI(register_android_os_NativeHandle),
-    REG_JNI(register_android_os_storage_StorageManager),
-    REG_JNI(register_android_os_VintfObject),
-    REG_JNI(register_android_os_VintfRuntimeInfo),
-    REG_JNI(register_android_service_DataLoaderService),
-    REG_JNI(register_android_view_DisplayEventReceiver),
-    REG_JNI(register_android_view_RenderNodeAnimator),
-    REG_JNI(register_android_view_InputApplicationHandle),
-    REG_JNI(register_android_view_InputWindowHandle),
-    REG_JNI(register_android_view_Surface),
-    REG_JNI(register_android_view_SurfaceControl),
-    REG_JNI(register_android_view_SurfaceSession),
-    REG_JNI(register_android_view_CompositionSamplingListener),
-    REG_JNI(register_android_view_TextureView),
-    REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
-    REG_JNI(register_com_google_android_gles_jni_EGLImpl),
-    REG_JNI(register_com_google_android_gles_jni_GLImpl),
-    REG_JNI(register_android_opengl_jni_EGL14),
-    REG_JNI(register_android_opengl_jni_EGL15),
-    REG_JNI(register_android_opengl_jni_EGLExt),
-    REG_JNI(register_android_opengl_jni_GLES10),
-    REG_JNI(register_android_opengl_jni_GLES10Ext),
-    REG_JNI(register_android_opengl_jni_GLES11),
-    REG_JNI(register_android_opengl_jni_GLES11Ext),
-    REG_JNI(register_android_opengl_jni_GLES20),
-    REG_JNI(register_android_opengl_jni_GLES30),
-    REG_JNI(register_android_opengl_jni_GLES31),
-    REG_JNI(register_android_opengl_jni_GLES31Ext),
-    REG_JNI(register_android_opengl_jni_GLES32),
-    REG_JNI(register_android_graphics_classes),
-    REG_JNI(register_android_graphics_BLASTBufferQueue),
-    REG_JNI(register_android_graphics_GraphicBuffer),
-    REG_JNI(register_android_database_CursorWindow),
-    REG_JNI(register_android_database_SQLiteConnection),
-    REG_JNI(register_android_database_SQLiteGlobal),
-    REG_JNI(register_android_database_SQLiteDebug),
-    REG_JNI(register_android_os_Debug),
-    REG_JNI(register_android_os_FileObserver),
-    REG_JNI(register_android_os_GraphicsEnvironment),
-    REG_JNI(register_android_os_MessageQueue),
-    REG_JNI(register_android_os_SELinux),
-    REG_JNI(register_android_os_Trace),
-    REG_JNI(register_android_os_UEventObserver),
-    REG_JNI(register_android_net_LocalSocketImpl),
-    REG_JNI(register_android_net_NetworkUtils),
-    REG_JNI(register_android_os_MemoryFile),
-    REG_JNI(register_android_os_SharedMemory),
-    REG_JNI(register_android_os_incremental_IncrementalManager),
-    REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
-    REG_JNI(register_com_android_internal_os_Zygote),
-    REG_JNI(register_com_android_internal_os_ZygoteInit),
-    REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
-    REG_JNI(register_android_hardware_Camera),
-    REG_JNI(register_android_hardware_camera2_CameraMetadata),
-    REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
-    REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
-    REG_JNI(register_android_hardware_camera2_DngCreator),
-    REG_JNI(register_android_hardware_HardwareBuffer),
-    REG_JNI(register_android_hardware_SensorManager),
-    REG_JNI(register_android_hardware_SerialPort),
-    REG_JNI(register_android_hardware_UsbDevice),
-    REG_JNI(register_android_hardware_UsbDeviceConnection),
-    REG_JNI(register_android_hardware_UsbRequest),
-    REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
-    REG_JNI(register_android_media_AudioDeviceAddress),
-    REG_JNI(register_android_media_AudioEffectDescriptor),
-    REG_JNI(register_android_media_AudioSystem),
-    REG_JNI(register_android_media_AudioRecord),
-    REG_JNI(register_android_media_AudioTrack),
-    REG_JNI(register_android_media_AudioAttributes),
-    REG_JNI(register_android_media_AudioProductStrategies),
-    REG_JNI(register_android_media_AudioVolumeGroups),
-    REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
-    REG_JNI(register_android_media_MediaMetrics),
-    REG_JNI(register_android_media_MicrophoneInfo),
-    REG_JNI(register_android_media_RemoteDisplay),
-    REG_JNI(register_android_media_ToneGenerator),
-    REG_JNI(register_android_media_midi),
+        REG_JNI(register_com_android_internal_os_RuntimeInit),
+        REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
+        REG_JNI(register_android_os_SystemClock),
+        REG_JNI(register_android_util_EventLog),
+        REG_JNI(register_android_util_Log),
+        REG_JNI(register_android_util_MemoryIntArray),
+        REG_JNI(register_android_util_StatsLog),
+        REG_JNI(register_android_util_StatsLogInternal),
+        REG_JNI(register_android_app_admin_SecurityLog),
+        REG_JNI(register_android_content_AssetManager),
+        REG_JNI(register_android_content_StringBlock),
+        REG_JNI(register_android_content_XmlBlock),
+        REG_JNI(register_android_content_res_ApkAssets),
+        REG_JNI(register_android_text_AndroidCharacter),
+        REG_JNI(register_android_text_Hyphenator),
+        REG_JNI(register_android_view_InputDevice),
+        REG_JNI(register_android_view_KeyCharacterMap),
+        REG_JNI(register_android_os_Process),
+        REG_JNI(register_android_os_SystemProperties),
+        REG_JNI(register_android_os_Binder),
+        REG_JNI(register_android_os_Parcel),
+        REG_JNI(register_android_os_HidlMemory),
+        REG_JNI(register_android_os_HidlSupport),
+        REG_JNI(register_android_os_HwBinder),
+        REG_JNI(register_android_os_HwBlob),
+        REG_JNI(register_android_os_HwParcel),
+        REG_JNI(register_android_os_HwRemoteBinder),
+        REG_JNI(register_android_os_NativeHandle),
+        REG_JNI(register_android_os_storage_StorageManager),
+        REG_JNI(register_android_os_VintfObject),
+        REG_JNI(register_android_os_VintfRuntimeInfo),
+        REG_JNI(register_android_service_DataLoaderService),
+        REG_JNI(register_android_view_DisplayEventReceiver),
+        REG_JNI(register_android_view_RenderNodeAnimator),
+        REG_JNI(register_android_view_InputApplicationHandle),
+        REG_JNI(register_android_view_InputWindowHandle),
+        REG_JNI(register_android_view_Surface),
+        REG_JNI(register_android_view_SurfaceControl),
+        REG_JNI(register_android_view_SurfaceSession),
+        REG_JNI(register_android_view_CompositionSamplingListener),
+        REG_JNI(register_android_view_TextureView),
+        REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
+        REG_JNI(register_com_google_android_gles_jni_EGLImpl),
+        REG_JNI(register_com_google_android_gles_jni_GLImpl),
+        REG_JNI(register_android_opengl_jni_EGL14),
+        REG_JNI(register_android_opengl_jni_EGL15),
+        REG_JNI(register_android_opengl_jni_EGLExt),
+        REG_JNI(register_android_opengl_jni_GLES10),
+        REG_JNI(register_android_opengl_jni_GLES10Ext),
+        REG_JNI(register_android_opengl_jni_GLES11),
+        REG_JNI(register_android_opengl_jni_GLES11Ext),
+        REG_JNI(register_android_opengl_jni_GLES20),
+        REG_JNI(register_android_opengl_jni_GLES30),
+        REG_JNI(register_android_opengl_jni_GLES31),
+        REG_JNI(register_android_opengl_jni_GLES31Ext),
+        REG_JNI(register_android_opengl_jni_GLES32),
+        REG_JNI(register_android_graphics_classes),
+        REG_JNI(register_android_graphics_BLASTBufferQueue),
+        REG_JNI(register_android_graphics_GraphicBuffer),
+        REG_JNI(register_android_database_CursorWindow),
+        REG_JNI(register_android_database_SQLiteConnection),
+        REG_JNI(register_android_database_SQLiteGlobal),
+        REG_JNI(register_android_database_SQLiteDebug),
+        REG_JNI(register_android_os_Debug),
+        REG_JNI(register_android_os_FileObserver),
+        REG_JNI(register_android_os_GraphicsEnvironment),
+        REG_JNI(register_android_os_MessageQueue),
+        REG_JNI(register_android_os_SELinux),
+        REG_JNI(register_android_os_Trace),
+        REG_JNI(register_android_os_UEventObserver),
+        REG_JNI(register_android_net_LocalSocketImpl),
+        REG_JNI(register_android_net_NetworkUtils),
+        REG_JNI(register_android_os_MemoryFile),
+        REG_JNI(register_android_os_SharedMemory),
+        REG_JNI(register_android_os_incremental_IncrementalManager),
+        REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
+        REG_JNI(register_com_android_internal_os_Zygote),
+        REG_JNI(register_com_android_internal_os_ZygoteInit),
+        REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
+        REG_JNI(register_android_hardware_Camera),
+        REG_JNI(register_android_hardware_camera2_CameraMetadata),
+        REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),
+        REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),
+        REG_JNI(register_android_hardware_camera2_DngCreator),
+        REG_JNI(register_android_hardware_HardwareBuffer),
+        REG_JNI(register_android_hardware_SensorManager),
+        REG_JNI(register_android_hardware_SerialPort),
+        REG_JNI(register_android_hardware_UsbDevice),
+        REG_JNI(register_android_hardware_UsbDeviceConnection),
+        REG_JNI(register_android_hardware_UsbRequest),
+        REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),
+        REG_JNI(register_android_media_AudioDevice),
+        REG_JNI(register_android_media_AudioEffectDescriptor),
+        REG_JNI(register_android_media_AudioSystem),
+        REG_JNI(register_android_media_AudioRecord),
+        REG_JNI(register_android_media_AudioTrack),
+        REG_JNI(register_android_media_AudioAttributes),
+        REG_JNI(register_android_media_AudioProductStrategies),
+        REG_JNI(register_android_media_AudioVolumeGroups),
+        REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
+        REG_JNI(register_android_media_MediaMetrics),
+        REG_JNI(register_android_media_MicrophoneInfo),
+        REG_JNI(register_android_media_RemoteDisplay),
+        REG_JNI(register_android_media_ToneGenerator),
+        REG_JNI(register_android_media_midi),
 
-    REG_JNI(register_android_opengl_classes),
-    REG_JNI(register_android_server_NetworkManagementSocketTagger),
-    REG_JNI(register_android_ddm_DdmHandleNativeHeap),
-    REG_JNI(register_android_backup_BackupDataInput),
-    REG_JNI(register_android_backup_BackupDataOutput),
-    REG_JNI(register_android_backup_FileBackupHelperBase),
-    REG_JNI(register_android_backup_BackupHelperDispatcher),
-    REG_JNI(register_android_app_backup_FullBackup),
-    REG_JNI(register_android_app_Activity),
-    REG_JNI(register_android_app_ActivityThread),
-    REG_JNI(register_android_app_NativeActivity),
-    REG_JNI(register_android_util_jar_StrictJarFile),
-    REG_JNI(register_android_view_InputChannel),
-    REG_JNI(register_android_view_InputEventReceiver),
-    REG_JNI(register_android_view_InputEventSender),
-    REG_JNI(register_android_view_InputQueue),
-    REG_JNI(register_android_view_KeyEvent),
-    REG_JNI(register_android_view_MotionEvent),
-    REG_JNI(register_android_view_PointerIcon),
-    REG_JNI(register_android_view_VelocityTracker),
+        REG_JNI(register_android_opengl_classes),
+        REG_JNI(register_android_server_NetworkManagementSocketTagger),
+        REG_JNI(register_android_ddm_DdmHandleNativeHeap),
+        REG_JNI(register_android_backup_BackupDataInput),
+        REG_JNI(register_android_backup_BackupDataOutput),
+        REG_JNI(register_android_backup_FileBackupHelperBase),
+        REG_JNI(register_android_backup_BackupHelperDispatcher),
+        REG_JNI(register_android_app_backup_FullBackup),
+        REG_JNI(register_android_app_Activity),
+        REG_JNI(register_android_app_ActivityThread),
+        REG_JNI(register_android_app_NativeActivity),
+        REG_JNI(register_android_util_jar_StrictJarFile),
+        REG_JNI(register_android_view_InputChannel),
+        REG_JNI(register_android_view_InputEventReceiver),
+        REG_JNI(register_android_view_InputEventSender),
+        REG_JNI(register_android_view_InputQueue),
+        REG_JNI(register_android_view_KeyEvent),
+        REG_JNI(register_android_view_MotionEvent),
+        REG_JNI(register_android_view_PointerIcon),
+        REG_JNI(register_android_view_VelocityTracker),
 
-    REG_JNI(register_android_content_res_ObbScanner),
-    REG_JNI(register_android_content_res_Configuration),
+        REG_JNI(register_android_content_res_ObbScanner),
+        REG_JNI(register_android_content_res_Configuration),
 
-    REG_JNI(register_android_animation_PropertyValuesHolder),
-    REG_JNI(register_android_security_Scrypt),
-    REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
-    REG_JNI(register_com_android_internal_os_FuseAppLoop),
+        REG_JNI(register_android_animation_PropertyValuesHolder),
+        REG_JNI(register_android_security_Scrypt),
+        REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
+        REG_JNI(register_com_android_internal_os_FuseAppLoop),
 };
 
 /*
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 8fc6afa..2a56fd6 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -34,9 +34,9 @@
 
 #include <hwui/MinikinSkia.h>
 #include <hwui/Typeface.h>
-#include <utils/FatVector.h>
 #include <minikin/FontFamily.h>
 #include <minikin/LocaleList.h>
+#include <ui/FatVector.h>
 
 #include <memory>
 
@@ -109,7 +109,7 @@
 
 static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
         jint weight, jint italic) {
-    uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    FatVector<SkFontArguments::Axis, 2> skiaAxes;
     for (const auto& axis : builder->axes) {
         skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
     }
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index aa209cb..38fb8bd 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -569,8 +569,8 @@
     // mRecycledBitmap specifies the width and height of the bitmap that we
     // want to reuse.  Neither can be changed.  We will try to find a way
     // to reuse the memory.
-    const int maxWidth = SkTMax(bitmap->width(), mRecycledBitmap->info().width());
-    const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height());
+    const int maxWidth = std::max(bitmap->width(), mRecycledBitmap->info().width());
+    const int maxHeight = std::max(bitmap->height(), mRecycledBitmap->info().height());
     const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight);
     const size_t rowBytes = maxInfo.minRowBytes();
     const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes);
diff --git a/core/jni/android/graphics/GraphicsStatsService.cpp b/core/jni/android/graphics/GraphicsStatsService.cpp
new file mode 100644
index 0000000..ef0aacc
--- /dev/null
+++ b/core/jni/android/graphics/GraphicsStatsService.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "GraphicsStatsService"
+
+#include <JankTracker.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <service/GraphicsStatsService.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+#include "core_jni_helpers.h"
+
+namespace android {
+
+using namespace android::uirenderer;
+
+static jint getAshmemSize(JNIEnv*, jobject) {
+    return sizeof(ProfileData);
+}
+
+static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
+    GraphicsStatsService::Dump* dump =
+            GraphicsStatsService::createDump(fd,
+                                             isProto ? GraphicsStatsService::DumpType::Protobuf
+                                                     : GraphicsStatsService::DumpType::Text);
+    return reinterpret_cast<jlong>(dump);
+}
+
+static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
+                      jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    std::string path;
+    const ProfileData* data = nullptr;
+    LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
+    ScopedByteArrayRO buffer{env};
+    if (jdata != nullptr) {
+        buffer.reset(jdata);
+        LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                            "Buffer size %zu doesn't match expected %zu!", buffer.size(),
+                            sizeof(ProfileData));
+        data = reinterpret_cast<const ProfileData*>(buffer.get());
+    }
+    if (jpath != nullptr) {
+        ScopedUtfChars pathChars(env, jpath);
+        LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(),
+                            "Failed to get path chars");
+        path.assign(pathChars.c_str(), pathChars.size());
+    }
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
+                        "Failed to get path chars");
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
+
+    const std::string package(packageChars.c_str(), packageChars.size());
+    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
+}
+
+static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    const std::string path(pathChars.c_str(), pathChars.size());
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::addToDump(dump, path);
+}
+
+static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    GraphicsStatsService::finishDump(dump);
+}
+
+static void finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr, jlong pulledData,
+                               jboolean lastFullDay) {
+    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
+    AStatsEventList* data = reinterpret_cast<AStatsEventList*>(pulledData);
+    GraphicsStatsService::finishDumpInMemory(dump, data, lastFullDay == JNI_TRUE);
+}
+
+static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
+                       jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
+    ScopedByteArrayRO buffer(env, jdata);
+    LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
+                        "Buffer size %zu doesn't match expected %zu!", buffer.size(),
+                        sizeof(ProfileData));
+    ScopedUtfChars pathChars(env, jpath);
+    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
+    ScopedUtfChars packageChars(env, jpackage);
+    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(),
+                        "Failed to get path chars");
+
+    const std::string path(pathChars.c_str(), pathChars.size());
+    const std::string package(packageChars.c_str(), packageChars.size());
+    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
+    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
+}
+
+static jobject gGraphicsStatsServiceObject = nullptr;
+static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
+
+static JNIEnv* getJNIEnv() {
+    JavaVM* vm = AndroidRuntime::getJavaVM();
+    JNIEnv* env = nullptr;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+        }
+    }
+    return env;
+}
+
+// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
+static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
+                                                                      AStatsEventList* data,
+                                                                      void* cookie) {
+    JNIEnv* env = getJNIEnv();
+    if (!env) {
+        return false;
+    }
+    if (gGraphicsStatsServiceObject == nullptr) {
+        ALOGE("Failed to get graphicsstats service");
+        return AStatsManager_PULL_SKIP;
+    }
+
+    for (bool lastFullDay : {true, false}) {
+        env->CallVoidMethod(gGraphicsStatsServiceObject,
+                            gGraphicsStatsService_pullGraphicsStatsMethodID,
+                            (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE),
+                            reinterpret_cast<jlong>(data));
+        if (env->ExceptionCheck()) {
+            env->ExceptionDescribe();
+            env->ExceptionClear();
+            ALOGE("Failed to invoke graphicsstats service");
+            return AStatsManager_PULL_SKIP;
+        }
+    }
+    return AStatsManager_PULL_SUCCESS;
+}
+
+// Register a puller for GRAPHICS_STATS atom with the statsd service.
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+    gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
+    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
+    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000);  // 10 milliseconds
+    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
+
+    AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
+                                           &graphicsStatsPullCallback, metadata, nullptr);
+
+    AStatsManager_PullAtomMetadata_release(metadata);
+}
+
+static void nativeDestructor(JNIEnv* env, jobject javaObject) {
+    AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
+    env->DeleteGlobalRef(gGraphicsStatsServiceObject);
+    gGraphicsStatsServiceObject = nullptr;
+}
+
+static const JNINativeMethod sMethods[] =
+        {{"nGetAshmemSize", "()I", (void*)getAshmemSize},
+         {"nCreateDump", "(IZ)J", (void*)createDump},
+         {"nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)addToDump},
+         {"nAddToDump", "(JLjava/lang/String;)V", (void*)addFileToDump},
+         {"nFinishDump", "(J)V", (void*)finishDump},
+         {"nFinishDumpInMemory", "(JJZ)V", (void*)finishDumpInMemory},
+         {"nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*)saveBuffer},
+         {"nativeInit", "()V", (void*)nativeInit},
+         {"nativeDestructor", "()V", (void*)nativeDestructor}};
+
+int register_android_graphics_GraphicsStatsService(JNIEnv* env) {
+    jclass graphicsStatsService_class =
+            FindClassOrDie(env, "android/graphics/GraphicsStatsService");
+    gGraphicsStatsService_pullGraphicsStatsMethodID =
+            GetMethodIDOrDie(env, graphicsStatsService_class, "pullGraphicsStats", "(ZJ)V");
+    return jniRegisterNativeMethods(env, "android/graphics/GraphicsStatsService", sMethods,
+                                    NELEM(sMethods));
+}
+
+} // namespace android
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
index bb0654d..8d84e87 100644
--- a/core/jni/android/graphics/fonts/Font.cpp
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -33,8 +33,8 @@
 
 #include <hwui/MinikinSkia.h>
 #include <hwui/Typeface.h>
-#include <utils/FatVector.h>
 #include <minikin/FontFamily.h>
+#include <ui/FatVector.h>
 
 #include <memory>
 
@@ -157,7 +157,7 @@
     sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
             release_global_ref, reinterpret_cast<void*>(fontRef)));
 
-    uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+    FatVector<SkFontArguments::Axis, 2> skiaAxes;
     for (const auto& axis : builder->axes) {
         skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
     }
diff --git a/core/jni/android_media_AudioDevice.cpp b/core/jni/android_media_AudioDevice.cpp
new file mode 100644
index 0000000..f6a0e4b
--- /dev/null
+++ b/core/jni/android_media_AudioDevice.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "android_media_AudioDevice.h"
+#include "android_media_AudioErrors.h"
+#include "core_jni_helpers.h"
+
+#include <media/AudioDeviceTypeAddr.h>
+
+using namespace android;
+
+static jclass gAudioDeviceClass;
+static jmethodID gAudioDeviceCstor;
+
+namespace android {
+
+jint createAudioDeviceFromNative(JNIEnv *env, jobject *jAudioDevice,
+                                 const AudioDeviceTypeAddr *devTypeAddr) {
+    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
+    jint jNativeType = (jint)devTypeAddr->mType;
+    ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
+
+    *jAudioDevice =
+            env->NewObject(gAudioDeviceClass, gAudioDeviceCstor, jNativeType, jAddress.get());
+
+    return jStatus;
+}
+
+} // namespace android
+
+int register_android_media_AudioDevice(JNIEnv *env) {
+    jclass audioDeviceTypeAddressClass = FindClassOrDie(env, "android/media/AudioDevice");
+    gAudioDeviceClass = MakeGlobalRefOrDie(env, audioDeviceTypeAddressClass);
+    gAudioDeviceCstor =
+            GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>", "(ILjava/lang/String;)V");
+
+    return 0;
+}
diff --git a/core/jni/android_media_AudioDeviceAddress.h b/core/jni/android_media_AudioDevice.h
similarity index 71%
rename from core/jni/android_media_AudioDeviceAddress.h
rename to core/jni/android_media_AudioDevice.h
index c66b179..fc92334 100644
--- a/core/jni/android_media_AudioDeviceAddress.h
+++ b/core/jni/android_media_AudioDevice.h
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_MEDIA_AUDIODEVICEADDRESS_H
-#define ANDROID_MEDIA_AUDIODEVICEADDRESS_H
+#ifndef ANDROID_MEDIA_AUDIODEVICE_H
+#define ANDROID_MEDIA_AUDIODEVICE_H
 
-#include <system/audio.h>
 #include <media/AudioDeviceTypeAddr.h>
+#include <system/audio.h>
 
 #include "jni.h"
 
 namespace android {
 
-// Create a Java AudioDeviceAddress instance from a C++ AudioDeviceTypeAddress
+// Create a Java AudioDevice instance from a C++ AudioDeviceTypeAddress
 
-extern jint createAudioDeviceAddressFromNative(JNIEnv *env, jobject *jAudioDeviceAddress,
-        const AudioDeviceTypeAddr *devTypeAddr);
+extern jint createAudioDeviceFromNative(JNIEnv *env, jobject *jAudioDevice,
+                                        const AudioDeviceTypeAddr *devTypeAddr);
 } // namespace android
 
 #endif
\ No newline at end of file
diff --git a/core/jni/android_media_AudioDeviceAddress.cpp b/core/jni/android_media_AudioDeviceAddress.cpp
deleted file mode 100644
index 5f39f7e..0000000
--- a/core/jni/android_media_AudioDeviceAddress.cpp
+++ /dev/null
@@ -1,53 +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.
- */
-
-#include "core_jni_helpers.h"
-#include "android_media_AudioDeviceAddress.h"
-#include "android_media_AudioErrors.h"
-
-#include <media/AudioDeviceTypeAddr.h>
-
-using namespace android;
-
-static jclass gAudioDeviceAddressClass;
-static jmethodID gAudioDeviceAddressCstor;
-
-namespace android {
-
-jint createAudioDeviceAddressFromNative(
-        JNIEnv *env, jobject *jAudioDeviceAddress,
-        const AudioDeviceTypeAddr *devTypeAddr) {
-    jint jStatus = (jint)AUDIO_JAVA_SUCCESS;
-    jint jNativeType = (jint)devTypeAddr->mType;
-    ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(devTypeAddr->mAddress.data()));
-
-    *jAudioDeviceAddress = env->NewObject(gAudioDeviceAddressClass, gAudioDeviceAddressCstor,
-            jNativeType, jAddress.get());
-
-    return jStatus;
-}
-
-}
-
-int register_android_media_AudioDeviceAddress(JNIEnv *env)
-{
-    jclass audioDeviceTypeAddressClass = FindClassOrDie(env, "android/media/AudioDeviceAddress");
-    gAudioDeviceAddressClass = MakeGlobalRefOrDie(env, audioDeviceTypeAddressClass);
-    gAudioDeviceAddressCstor = GetMethodIDOrDie(env, audioDeviceTypeAddressClass, "<init>",
-                                                "(ILjava/lang/String;)V");
-
-    return 0;
-}
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 0156e23..b4590f4 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -26,12 +26,6 @@
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
 
-#include "android_media_AudioAttributes.h"
-#include "android_media_AudioDeviceAddress.h"
-#include "android_media_AudioEffectDescriptor.h"
-#include "android_media_AudioErrors.h"
-#include "android_media_AudioFormat.h"
-#include "android_media_MicrophoneInfo.h"
 #include <audiomanager/AudioManager.h>
 #include <media/AudioPolicy.h>
 #include <media/AudioSystem.h>
@@ -39,6 +33,12 @@
 #include <nativehelper/ScopedLocalRef.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
+#include "android_media_AudioAttributes.h"
+#include "android_media_AudioDevice.h"
+#include "android_media_AudioEffectDescriptor.h"
+#include "android_media_AudioErrors.h"
+#include "android_media_AudioFormat.h"
+#include "android_media_MicrophoneInfo.h"
 
 // ----------------------------------------------------------------------------
 
@@ -2349,7 +2349,7 @@
         jint strategy, jobjectArray jDeviceArray)
 {
     if (jDeviceArray == nullptr || env->GetArrayLength(jDeviceArray) != 1) {
-        ALOGE("%s invalid array to store AudioDeviceAddress", __FUNCTION__);
+        ALOGE("%s invalid array to store AudioDevice", __FUNCTION__);
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
@@ -2359,10 +2359,10 @@
     if (status != NO_ERROR) {
         return (jint) status;
     }
-    jobject jAudioDeviceAddress = NULL;
-    jint jStatus = createAudioDeviceAddressFromNative(env, &jAudioDeviceAddress, &elDevice);
+    jobject jAudioDevice = NULL;
+    jint jStatus = createAudioDeviceFromNative(env, &jAudioDevice, &elDevice);
     if (jStatus == AUDIO_JAVA_SUCCESS) {
-        env->SetObjectArrayElement(jDeviceArray, 0, jAudioDeviceAddress);
+        env->SetObjectArrayElement(jDeviceArray, 0, jAudioDevice);
     }
     return jStatus;
 }
@@ -2377,7 +2377,7 @@
     // 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__);
+        ALOGE("%s invalid array to store AudioDevice", __FUNCTION__);
         return (jint)AUDIO_JAVA_BAD_VALUE;
     }
 
@@ -2398,105 +2398,133 @@
         return AUDIO_JAVA_INVALID_OPERATION;
     }
     size_t index = 0;
-    jobject jAudioDeviceAddress = NULL;
+    jobject jAudioDevice = NULL;
     for (const auto& device : devices) {
-        jStatus = createAudioDeviceAddressFromNative(env, &jAudioDeviceAddress, &device);
+        jStatus = createAudioDeviceFromNative(env, &jAudioDevice, &device);
         if (jStatus != AUDIO_JAVA_SUCCESS) {
             return jStatus;
         }
-        env->SetObjectArrayElement(jDeviceArray, index++, jAudioDeviceAddress);
+        env->SetObjectArrayElement(jDeviceArray, index++, jAudioDevice);
     }
     return jStatus;
 }
 
 // ----------------------------------------------------------------------------
 
-static const JNINativeMethod gMethods[] = {
-    {"setParameters",        "(Ljava/lang/String;)I", (void *)android_media_AudioSystem_setParameters},
-    {"getParameters",        "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
-    {"muteMicrophone",      "(Z)I",     (void *)android_media_AudioSystem_muteMicrophone},
-    {"isMicrophoneMuted",   "()Z",      (void *)android_media_AudioSystem_isMicrophoneMuted},
-    {"isStreamActive",      "(II)Z",    (void *)android_media_AudioSystem_isStreamActive},
-    {"isStreamActiveRemotely","(II)Z",  (void *)android_media_AudioSystem_isStreamActiveRemotely},
-    {"isSourceActive",      "(I)Z",     (void *)android_media_AudioSystem_isSourceActive},
-    {"newAudioSessionId",   "()I",      (void *)android_media_AudioSystem_newAudioSessionId},
-    {"newAudioPlayerId",    "()I",      (void *)android_media_AudioSystem_newAudioPlayerId},
-    {"newAudioRecorderId",  "()I",      (void *)android_media_AudioSystem_newAudioRecorderId},
-    {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
-    {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
-    {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I", (void *)android_media_AudioSystem_handleDeviceConfigChange},
-    {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
-    {"setForceUse",         "(II)I",    (void *)android_media_AudioSystem_setForceUse},
-    {"getForceUse",         "(I)I",     (void *)android_media_AudioSystem_getForceUse},
-    {"initStreamVolume",    "(III)I",   (void *)android_media_AudioSystem_initStreamVolume},
-    {"setStreamVolumeIndex","(III)I",   (void *)android_media_AudioSystem_setStreamVolumeIndex},
-    {"getStreamVolumeIndex","(II)I",    (void *)android_media_AudioSystem_getStreamVolumeIndex},
-    {"setVolumeIndexForAttributes","(Landroid/media/AudioAttributes;II)I",   (void *)android_media_AudioSystem_setVolumeIndexForAttributes},
-    {"getVolumeIndexForAttributes","(Landroid/media/AudioAttributes;I)I",    (void *)android_media_AudioSystem_getVolumeIndexForAttributes},
-    {"getMinVolumeIndexForAttributes","(Landroid/media/AudioAttributes;)I",    (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes},
-    {"getMaxVolumeIndexForAttributes","(Landroid/media/AudioAttributes;)I",    (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes},
-    {"setMasterVolume",     "(F)I",     (void *)android_media_AudioSystem_setMasterVolume},
-    {"getMasterVolume",     "()F",      (void *)android_media_AudioSystem_getMasterVolume},
-    {"setMasterMute",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMute},
-    {"getMasterMute",       "()Z",      (void *)android_media_AudioSystem_getMasterMute},
-    {"setMasterMono",       "(Z)I",     (void *)android_media_AudioSystem_setMasterMono},
-    {"getMasterMono",       "()Z",      (void *)android_media_AudioSystem_getMasterMono},
-    {"setMasterBalance",    "(F)I",     (void *)android_media_AudioSystem_setMasterBalance},
-    {"getMasterBalance",    "()F",      (void *)android_media_AudioSystem_getMasterBalance},
-    {"getDevicesForStream", "(I)I",     (void *)android_media_AudioSystem_getDevicesForStream},
-    {"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
-    {"getPrimaryOutputFrameCount",   "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
-    {"getOutputLatency",    "(I)I",     (void *)android_media_AudioSystem_getOutputLatency},
-    {"setLowRamDevice",     "(ZJ)I",    (void *)android_media_AudioSystem_setLowRamDevice},
-    {"checkAudioFlinger",    "()I",     (void *)android_media_AudioSystem_checkAudioFlinger},
-    {"listAudioPorts",      "(Ljava/util/ArrayList;[I)I",
-                                                (void *)android_media_AudioSystem_listAudioPorts},
-    {"createAudioPatch",    "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I",
-                                            (void *)android_media_AudioSystem_createAudioPatch},
-    {"releaseAudioPatch",   "(Landroid/media/AudioPatch;)I",
-                                            (void *)android_media_AudioSystem_releaseAudioPatch},
-    {"listAudioPatches",    "(Ljava/util/ArrayList;[I)I",
-                                                (void *)android_media_AudioSystem_listAudioPatches},
-    {"setAudioPortConfig",   "(Landroid/media/AudioPortConfig;)I",
-                                            (void *)android_media_AudioSystem_setAudioPortConfig},
-    {"startAudioSource",    "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
-                                            (void *)android_media_AudioSystem_startAudioSource},
-    {"stopAudioSource",     "(I)I", (void *)android_media_AudioSystem_stopAudioSource},
-    {"getAudioHwSyncForSession", "(I)I",
-                                    (void *)android_media_AudioSystem_getAudioHwSyncForSession},
-    {"registerPolicyMixes",    "(Ljava/util/ArrayList;Z)I",
-                                            (void *)android_media_AudioSystem_registerPolicyMixes},
-    {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
-                                        (void *)android_media_AudioSystem_setUidDeviceAffinities},
-    {"removeUidDeviceAffinities", "(I)I",
-                                        (void *)android_media_AudioSystem_removeUidDeviceAffinities},
-    {"native_register_dynamic_policy_callback", "()V",
-                                    (void *)android_media_AudioSystem_registerDynPolicyCallback},
-    {"native_register_recording_callback", "()V",
-                                    (void *)android_media_AudioSystem_registerRecordingCallback},
-    {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
-    {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
-    {"native_is_offload_supported", "(IIIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
-    {"getMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_getMicrophones},
-    {"getSurroundFormats", "(Ljava/util/Map;Z)I", (void *)android_media_AudioSystem_getSurroundFormats},
-    {"setSurroundFormatEnabled", "(IZ)I", (void *)android_media_AudioSystem_setSurroundFormatEnabled},
-    {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
-    {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
-    {"isHapticPlaybackSupported", "()Z", (void *)android_media_AudioSystem_isHapticPlaybackSupported},
-    {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I",
-                    (void*)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP},
-    {"setSupportedSystemUsages", "([I)I", (void *)android_media_AudioSystem_setSupportedSystemUsages},
-    {"setAllowedCapturePolicy", "(II)I", (void *)android_media_AudioSystem_setAllowedCapturePolicy},
-    {"setRttEnabled",       "(Z)I",     (void *)android_media_AudioSystem_setRttEnabled},
-    {"setAudioHalPids",  "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
-    {"isCallScreeningModeSupported", "()Z", (void *)android_media_AudioSystem_isCallScreeningModeSupported},
-    {"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},
-    {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", (void *)android_media_AudioSystem_setUserIdDeviceAffinities},
-    {"removeUserIdDeviceAffinities", "(I)I", (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}
-};
+static const JNINativeMethod gMethods[] =
+        {{"setParameters", "(Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setParameters},
+         {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;",
+          (void *)android_media_AudioSystem_getParameters},
+         {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone},
+         {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted},
+         {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive},
+         {"isStreamActiveRemotely", "(II)Z",
+          (void *)android_media_AudioSystem_isStreamActiveRemotely},
+         {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive},
+         {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId},
+         {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId},
+         {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId},
+         {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I",
+          (void *)android_media_AudioSystem_setDeviceConnectionState},
+         {"getDeviceConnectionState", "(ILjava/lang/String;)I",
+          (void *)android_media_AudioSystem_getDeviceConnectionState},
+         {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I",
+          (void *)android_media_AudioSystem_handleDeviceConfigChange},
+         {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
+         {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
+         {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
+         {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
+         {"setStreamVolumeIndex", "(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex},
+         {"getStreamVolumeIndex", "(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex},
+         {"setVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;II)I",
+          (void *)android_media_AudioSystem_setVolumeIndexForAttributes},
+         {"getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I",
+          (void *)android_media_AudioSystem_getVolumeIndexForAttributes},
+         {"getMinVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I",
+          (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes},
+         {"getMaxVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I",
+          (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes},
+         {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume},
+         {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume},
+         {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute},
+         {"getMasterMute", "()Z", (void *)android_media_AudioSystem_getMasterMute},
+         {"setMasterMono", "(Z)I", (void *)android_media_AudioSystem_setMasterMono},
+         {"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono},
+         {"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance},
+         {"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance},
+         {"getDevicesForStream", "(I)I", (void *)android_media_AudioSystem_getDevicesForStream},
+         {"getPrimaryOutputSamplingRate", "()I",
+          (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
+         {"getPrimaryOutputFrameCount", "()I",
+          (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
+         {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency},
+         {"setLowRamDevice", "(ZJ)I", (void *)android_media_AudioSystem_setLowRamDevice},
+         {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger},
+         {"listAudioPorts", "(Ljava/util/ArrayList;[I)I",
+          (void *)android_media_AudioSystem_listAudioPorts},
+         {"createAudioPatch",
+          "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/"
+          "AudioPortConfig;)I",
+          (void *)android_media_AudioSystem_createAudioPatch},
+         {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I",
+          (void *)android_media_AudioSystem_releaseAudioPatch},
+         {"listAudioPatches", "(Ljava/util/ArrayList;[I)I",
+          (void *)android_media_AudioSystem_listAudioPatches},
+         {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I",
+          (void *)android_media_AudioSystem_setAudioPortConfig},
+         {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I",
+          (void *)android_media_AudioSystem_startAudioSource},
+         {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource},
+         {"getAudioHwSyncForSession", "(I)I",
+          (void *)android_media_AudioSystem_getAudioHwSyncForSession},
+         {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I",
+          (void *)android_media_AudioSystem_registerPolicyMixes},
+         {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setUidDeviceAffinities},
+         {"removeUidDeviceAffinities", "(I)I",
+          (void *)android_media_AudioSystem_removeUidDeviceAffinities},
+         {"native_register_dynamic_policy_callback", "()V",
+          (void *)android_media_AudioSystem_registerDynPolicyCallback},
+         {"native_register_recording_callback", "()V",
+          (void *)android_media_AudioSystem_registerRecordingCallback},
+         {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
+         {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
+         {"native_is_offload_supported", "(IIIII)Z",
+          (void *)android_media_AudioSystem_isOffloadSupported},
+         {"getMicrophones", "(Ljava/util/ArrayList;)I",
+          (void *)android_media_AudioSystem_getMicrophones},
+         {"getSurroundFormats", "(Ljava/util/Map;Z)I",
+          (void *)android_media_AudioSystem_getSurroundFormats},
+         {"setSurroundFormatEnabled", "(IZ)I",
+          (void *)android_media_AudioSystem_setSurroundFormatEnabled},
+         {"setAssistantUid", "(I)I", (void *)android_media_AudioSystem_setAssistantUid},
+         {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
+         {"isHapticPlaybackSupported", "()Z",
+          (void *)android_media_AudioSystem_isHapticPlaybackSupported},
+         {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I",
+          (void *)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP},
+         {"setSupportedSystemUsages", "([I)I",
+          (void *)android_media_AudioSystem_setSupportedSystemUsages},
+         {"setAllowedCapturePolicy", "(II)I",
+          (void *)android_media_AudioSystem_setAllowedCapturePolicy},
+         {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled},
+         {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids},
+         {"isCallScreeningModeSupported", "()Z",
+          (void *)android_media_AudioSystem_isCallScreeningModeSupported},
+         {"setPreferredDeviceForStrategy", "(IILjava/lang/String;)I",
+          (void *)android_media_AudioSystem_setPreferredDeviceForStrategy},
+         {"removePreferredDeviceForStrategy", "(I)I",
+          (void *)android_media_AudioSystem_removePreferredDeviceForStrategy},
+         {"getPreferredDeviceForStrategy", "(I[Landroid/media/AudioDevice;)I",
+          (void *)android_media_AudioSystem_getPreferredDeviceForStrategy},
+         {"getDevicesForAttributes",
+          "(Landroid/media/AudioAttributes;[Landroid/media/AudioDevice;)I",
+          (void *)android_media_AudioSystem_getDevicesForAttributes},
+         {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
+          (void *)android_media_AudioSystem_setUserIdDeviceAffinities},
+         {"removeUserIdDeviceAffinities", "(I)I",
+          (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}};
 
 static const JNINativeMethod gEventHandlerMethods[] = {
     {"native_setup",
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index 698062a..d41e982 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -26,6 +26,10 @@
 
 namespace android {
 
+static jboolean nativeIsEnabled(JNIEnv* env, jobject clazz) {
+    return IncFs_IsEnabled();
+}
+
 static jboolean nativeIsIncrementalPath(JNIEnv* env,
                                     jobject clazz,
                                     jstring javaPath) {
@@ -34,8 +38,8 @@
 }
 
 static const JNINativeMethod method_table[] = {
-        {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
-         (void*)nativeIsIncrementalPath},
+        {"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
+        {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
 };
 
 int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 69ca17c..5a8225c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -147,10 +147,12 @@
 }
 
 static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
-        jboolean translucent, jlong rootRenderNodePtr) {
+        jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) {
     RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
     ContextFactoryImpl factory(rootRenderNode);
-    return (jlong) new RenderProxy(translucent, rootRenderNode, &factory);
+    RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
+    proxy->setWideGamut(isWideGamut);
+    return (jlong) proxy;
 }
 
 static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
@@ -627,7 +629,7 @@
     { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
     { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
-    { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
+    { "nCreateProxy", "(ZZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
     { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties },
     { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName },
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index e23f150..6a49220 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -178,8 +178,8 @@
     }
 
     optional Importance importance = 10;
-    optional int32 pss = 11;
-    optional int32 rss = 12;
+    optional int64 pss = 11;
+    optional int64 rss = 12;
     optional int64 timestamp = 13;
     optional string description = 14;
 }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index fc0a2ef..4625418 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -708,6 +708,12 @@
 
     // ACTION: Deny "Access all files" for an app
     APP_SPECIAL_PERMISSION_MANAGE_EXT_STRG_DENY = 1731;
+
+    // ACTION: Battery feature usage
+    ACTION_BATTERY_OPTION_FEATURE_USAGE = 1732;
+
+    // ACTION: Battery feature runtime event
+    ACTION_BATTERY_OPTION_RUNTIME_EVENT = 1733;
 }
 
 /**
@@ -2578,4 +2584,11 @@
     // CATEGORY: SETTINGS
     // OS: R
     CONNECTION_DEVICE_ADVANCED_NFC = 1828;
+
+    // OPEN: Settings -> Apps & Notifications -> Special App Access
+    INTERACT_ACROSS_PROFILES = 1829;
+
+    // OPEN: Settings > Notifications > (app or conversations) > conversation
+    NOTIFICATION_CONVERSATION_SETTINGS = 1830;
+
 }
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 303d62d..546c5a0 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -219,12 +219,8 @@
     // The maximum number of background jobs we allow when the system is in a
     // critical memory state.
     optional int32 bg_critical_job_count = 14;
-    // The maximum number of times we allow a job to have itself rescheduled
-    // before giving up on it, for standard jobs.
-    optional int32 max_standard_reschedule_count = 15;
-    // The maximum number of times we allow a job to have itself rescheduled
-    // before giving up on it, for jobs that are executing work.
-    optional int32 max_work_reschedule_count = 16;
+    reserved 15; // max_standard_reschedule_count
+    reserved 16; // max_work_reschedule_count
     // The minimum backoff time to allow for linear backoff.
     optional int64 min_linear_backoff_time_ms = 17;
     // The minimum backoff time to allow for exponential backoff.
diff --git a/core/proto/android/service/OWNERS b/core/proto/android/service/OWNERS
new file mode 100644
index 0000000..70cb50f
--- /dev/null
+++ b/core/proto/android/service/OWNERS
@@ -0,0 +1 @@
+per-file sensor_service.proto = arthuri@google.com, bduddie@google.com, stange@google.com
diff --git a/core/proto/android/service/sensor_service.proto b/core/proto/android/service/sensor_service.proto
index 8598f86..48f6670 100644
--- a/core/proto/android/service/sensor_service.proto
+++ b/core/proto/android/service/sensor_service.proto
@@ -39,7 +39,7 @@
         OP_MODE_RESTRICTED = 2;
         OP_MODE_DATA_INJECTION = 3;
     }
-
+    optional sint32 init_status = 16;
     optional int64 current_time_ms = 1;
     optional SensorDeviceProto sensor_device = 2;
     optional SensorListProto sensors = 3;
@@ -56,6 +56,8 @@
     repeated SensorEventConnectionProto active_connections = 13;
     repeated SensorDirectConnectionProto direct_connections = 14;
     repeated SensorRegistrationInfoProto previous_registrations = 15;
+
+    // Next tag: 17
 }
 
 // Proto dump of android::SensorDevice
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 32a6cc3..ff69671 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1535,7 +1535,7 @@
          @hide
     -->
     <permission android:name="android.permission.ACCESS_CONTEXT_HUB"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signature|privileged" />
     <uses-permission android:name="android.permission.ACCESS_CONTEXT_HUB"/>
 
     <!-- @SystemApi Allows an application to create mock location providers for testing.
@@ -1998,17 +1998,17 @@
     <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
+    <!-- Allows TvInputService to access underlying TV input hardware such as
          built-in tuners and HDMI-in's.
-         @hide This should only be used by OEM's TvInputService's.
-    -->
+         <p>This should only be used by OEM's TvInputService's.
+         @hide @SystemApi -->
     <permission android:name="android.permission.TV_INPUT_HARDWARE"
         android:protectionLevel="signature|privileged|vendorPrivileged" />
 
-    <!-- @SystemApi Allows to capture a frame of TV input hardware such as
+    <!-- Allows to capture a frame of TV input hardware such as
          built-in tuners and HDMI-in's.
-         @hide <p>Not for use by third-party applications.
-    -->
+         <p>Not for use by third-party applications.
+         @hide @SystemApi -->
     <permission android:name="android.permission.CAPTURE_TV_INPUT"
         android:protectionLevel="signature|privileged" />
 
@@ -3433,10 +3433,10 @@
     <permission android:name="android.permission.READ_CONTENT_RATING_SYSTEMS"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi Allows an application to notify TV inputs by sending broadcasts.
+    <!-- Allows an application to notify TV inputs by sending broadcasts.
          <p>Protection level: signature|privileged
          <p>Not for use by third-party applications.
-         @hide -->
+         @hide @SystemApi -->
     <permission android:name="android.permission.NOTIFY_TV_INPUTS"
          android:protectionLevel="signature|privileged" />
 
@@ -3450,11 +3450,11 @@
 
     <!-- This permission is required by Media Resource Manager Service when
          accessing its overridePid Api.
-         <p>Protection level: signature|privileged
+         <p>Protection level: signature
          <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID"
-         android:protectionLevel="signature|privileged" />
+         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link android.media.routing.MediaRouteService}
          to ensure that only the system can interact with it.
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 46e8f64..b01eb39 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -1,31 +1,50 @@
-<!--
-Copyright (C) 2020 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="240dp"
-    android:height="240dp"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
+    android:width="512dp"
+    android:height="512dp"
+    android:viewportWidth="512"
+    android:viewportHeight="512">
     <path
-        android:fillColor="#000"
-        android:pathData="M16 4c-2.2 0-4 1.8-4 4v4H4V8c0-2.2 1.8-4 4-4h8z"/>
+        android:fillColor="#F86734"
+        android:pathData="M416.23 236.62h-10.67c-1.46 0-2.65-1.19-2.65-2.65v-9.85c0-1.47 1.19-2.65 2.65-2.65h23.37c1.47 0 2.66 1.19 2.66 2.65v66.9c0 1.46-1.2 2.65-2.66 2.65H418.9c-1.47 0-2.66-1.19-2.66-2.65v-54.4z"/>
     <path
-        android:fillColor="#000"
-        android:pathData="M8 20c2.2 0 4-1.8 4-4v-4H4v8h4z"/>
+        android:fillColor="#F86734"
+        android:pathData="M455.51 236.62h-10.67c-1.47 0-2.65-1.19-2.65-2.65v-9.85c0-1.47 1.18-2.65 2.65-2.65h23.37c1.47 0 2.66 1.19 2.66 2.65v66.9c0 1.46-1.2 2.65-2.66 2.65h-10.05c-1.46 0-2.65-1.19-2.65-2.65v-54.4z"/>
     <path
-        android:fillColor="#80000000"
-        android:pathData="M16 12c2.2 0 4-1.8 4-4V4h-8v8h4z"/>
+        android:fillColor="#D6F0FF"
+        android:pathData="M364.12 400.25a4.34 4.34 0 1 0 0 8.68a4.34 4.34 0 1 0 0-8.68z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M275.46 433.53a4.84 4.84 0 1 0 0 9.68a4.84 4.84 0 1 0 0-9.68z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M184.52 418.83a5.36 5.36 0 1 0 0 10.72a5.36 5.36 0 1 0 0-10.72z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M110.42 359.19a5.89 5.89 0 1 0 0 11.78a5.89 5.89 0 1 0 0-11.78z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M75.94 270.17a6.43 6.43 0 1 0 0 12.86a6.43 6.43 0 1 0 0-12.86z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M89.48 178.57a6.98 6.98 0 1 0 0 13.96a6.98 6.98 0 1 0 0-13.96z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M147.97 103.54a7.54 7.54 0 1 0 0 15.08a7.54 7.54 0 1 0 0-15.08z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M236.63 66.7a8.1 8.1 0 1 0 0 16.2a8.1 8.1 0 1 0 0-16.2z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M327.09 78.3a8.66 8.66 0 1 0 0 17.32a8.66 8.66 0 1 0 0-17.32z"/>
+    <path
+        android:fillColor="#D6F0FF"
+        android:pathData="M401.05 136.97a9.22 9.22 0 1 0 0 18.44a9.22 9.22 0 1 0 0-18.44z"/>
+    <group>
+        <path
+            android:fillColor="#3DDB85"
+            android:pathData="M255.45 129.46a128.11 128.11 0 1 0 0 256.22a128.11 128.11 0 1 0 0-256.22z"/>
+        <path
+            android:fillColor="#FFF"
+            android:pathData="M339.23 236.09a21.48 21.48 0 1 0 0 42.96a21.48 21.48 0 1 0 0-42.96z"/>
+    </group>
 </vector>
-
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 0e9aab2..700781b 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -1,17 +1,17 @@
-<!--
-Copyright (C) 2020 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Android Open Source Project
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
+     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
+          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.
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
@@ -19,12 +19,22 @@
     android:viewportWidth="24"
     android:viewportHeight="24">
     <path
-        android:fillColor="#000"
-        android:pathData="M16 4c-2.2 0-4 1.8-4 4v4H4V8c0-2.2 1.8-4 4-4h8z"/>
-    <path
-        android:fillColor="#000"
-        android:pathData="M8 20c2.2 0 4-1.8 4-4v-4H4v8h4z"/>
-    <path
-        android:fillColor="#80000000"
-        android:pathData="M16 12c2.2 0 4-1.8 4-4V4h-8v8h4z"/>
+        android:fillColor="#FFFFFF"
+        android:pathData="
+M 12,1
+A 11 11 0 0 0 1,12
+A 11 11 0 1 0 12,1
+Z
+
+M 12.5,8
+a 3,3 0 0 1 6,0
+a 3,3 0 0 1 -6,0
+Z
+
+M 5.5,8
+a 3,3 0 0 1 6,0
+l 0,8
+a 3,3 0 0 1 -6,0
+Z
+"/>
 </vector>
diff --git a/core/res/res/drawable/tab_indicator_resolver.xml b/core/res/res/drawable/tab_indicator_resolver.xml
new file mode 100644
index 0000000..ff16d81a
--- /dev/null
+++ b/core/res/res/drawable/tab_indicator_resolver.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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="nest">
+    <item>
+        <ripple android:color="@color/tab_highlight_material">
+            <item android:id="@id/mask">
+                <color android:color="@color/white" />
+            </item>
+        </ripple>
+    </item>
+    <item android:gravity="bottom">
+        <shape android:shape="rectangle"
+               android:tint="@color/resolver_tabs_active_color">
+            <size android:height="2dp" />
+            <solid android:color="@color/tab_indicator_material" />
+        </shape>
+    </item>
+    <item android:bottom="2dp"
+          android:drawable="@color/transparent" />
+</layer-list>
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index d6fd7aa..d19e313 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -20,7 +20,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:clickable="true"
     android:gravity="center"
     android:paddingStart="16dp"
     android:paddingEnd="16dp"
diff --git a/core/res/res/layout/tab_indicator_resolver.xml b/core/res/res/layout/tab_indicator_resolver.xml
new file mode 100644
index 0000000..2038da6
--- /dev/null
+++ b/core/res/res/layout/tab_indicator_resolver.xml
@@ -0,0 +1,38 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="?android:attr/actionBarSize"
+    android:orientation="horizontal"
+    style="@android:style/Widget.Material.Resolver.Tab">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:textAllCaps="false"
+        style="@android:style/Widget.Material.TabText" />
+
+</LinearLayout>
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index c5e72f0..33caeb6 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -32,4 +32,6 @@
     <color name="chooser_row_divider">@color/list_divider_color_dark</color>
     <color name="chooser_gradient_background">@color/loading_gradient_background_color_dark</color>
     <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
+
+    <color name="resolver_tabs_active_color">#FF8AB4F8</color>
 </resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index cef21db..81ec278 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1679,6 +1679,15 @@
         <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
         <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
         <item name="navigationBarColor">@android:color/transparent</item>
+        <item name="tabWidgetStyle">@style/Widget.DeviceDefault.Resolver.TabWidget</item>
+    </style>
+
+    <style name="Widget.DeviceDefault.Resolver.TabWidget" parent="Widget.DeviceDefault.TabWidget">
+        <item name="tabLayout">@layout/tab_indicator_resolver</item>
+    </style>
+
+    <style name="Widget.Material.Resolver.Tab" parent="Widget.Material.Tab">
+        <item name="background">@drawable/tab_indicator_resolver</item>
     </style>
 
     <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault.ResolverCommon">
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 0d7ac08..70917e7 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
          http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
 
     <!-- Arab Emirates -->
-    <shortcode country="ae" pattern="\\d{1,5}" free="3214|1017" />
+    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" />
 
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -184,7 +184,7 @@
     <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
 
     <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
-    <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" />
+    <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963" />
 
     <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
     <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
@@ -199,7 +199,7 @@
     <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
 
     <!-- New Zealand: 3-4 digits, known premium codes listed -->
-    <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
+    <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
 
     <!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
     <shortcode country="pe" pattern="\\d{4,5}" free="9963" />
@@ -267,4 +267,7 @@
     <!-- Mayotte (French Territory): 1-5 digits (not confirmed) -->
     <shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
 
+    <!-- South Africa -->
+    <shortcode country="za" pattern="\d{1,5}" free="44136" />
+
 </shortcodes>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 59335a5..718ca46 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1330,6 +1330,12 @@
             android:process=":FakeProvider">
         </provider>
 
+        <provider
+            android:name="android.content.SlowProvider"
+            android:authorities="android.content.SlowProvider"
+            android:process=":SlowProvider">
+        </provider>
+
         <!-- Application components used for os tests -->
 
         <service android:name="android.os.MessengerService"
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 0a21875..d1608d0 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -234,6 +234,40 @@
     }
 
     @Test
+    public void testRemoveNotificationFromWrite() {
+        NotificationHistory history = new NotificationHistory();
+
+        List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
+        List<String> postRemoveExpectedStrings = new ArrayList<>();
+        for (int i = 1; i <= 10; i++) {
+            HistoricalNotification n = getHistoricalNotification("pkg", i);
+
+            if (987654323 != n.getPostedTimeMs()) {
+                postRemoveExpectedStrings.add(n.getPackage());
+                postRemoveExpectedStrings.add(n.getChannelName());
+                postRemoveExpectedStrings.add(n.getChannelId());
+                postRemoveExpectedEntries.add(n);
+            }
+
+            history.addNotificationToWrite(n);
+        }
+
+        history.poolStringsFromNotifications();
+
+        assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
+        // 1 package name and 10 unique channel names and ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
+
+        history.removeNotificationFromWrite("pkg", 987654323);
+
+
+        // 1 package names and 9 * 2 unique channel names and ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(19);
+        assertThat(history.getNotificationsToWrite())
+                .containsExactlyElementsIn(postRemoveExpectedEntries);
+    }
+
+    @Test
     public void testParceling() {
         NotificationHistory history = new NotificationHistory();
 
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 9dcce1e..6dc7392 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -209,4 +209,13 @@
         String type = mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote"));
         assertEquals("fake/remote", type);
     }
+
+
+    @Test
+    public void testGetType_slowProvider() {
+        // This provider is running in a different process and is intentionally slow to start.
+        // We are trying to confirm that it does not cause an ANR
+        String type = mResolver.getType(Uri.parse("content://android.content.SlowProvider"));
+        assertEquals("slow", type);
+    }
 }
diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java
new file mode 100644
index 0000000..aba32e8
--- /dev/null
+++ b/core/tests/coretests/src/android/content/SlowProvider.java
@@ -0,0 +1,65 @@
+/*
+ * 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.content;
+
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * A dummy content provider for tests.  This provider runs in a different process from the test and
+ * is intentionally slow.
+ */
+public class SlowProvider extends ContentProvider {
+
+    private static final int ON_CREATE_LATENCY_MILLIS = 3000;
+
+    @Override
+    public boolean onCreate() {
+        try {
+            Thread.sleep(ON_CREATE_LATENCY_MILLIS);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return "slow";
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
index 7733559..3273e5d 100644
--- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
@@ -97,7 +97,7 @@
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE);
         assertThat(stringAtomicFormula.getValue()).matches(appCert);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
+        assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
     }
 
     @Test
@@ -110,7 +110,7 @@
         assertThat(stringAtomicFormula.getKey()).isEqualTo(
                 AtomicFormula.INSTALLER_CERTIFICATE);
         assertThat(stringAtomicFormula.getValue()).matches(installerCert);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
+        assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
index dc03167..75ef1f2 100644
--- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
@@ -54,7 +54,7 @@
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE);
         assertThat(stringAtomicFormula.getValue()).matches(appCertificate);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
+        assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
     }
 
     @Test
@@ -82,7 +82,7 @@
 
         assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_CERTIFICATE);
         assertThat(stringAtomicFormula.getValue()).matches(installerCertificate);
-        assertThat(stringAtomicFormula.getIsHashedValue()).isFalse();
+        assertThat(stringAtomicFormula.getIsHashedValue()).isTrue();
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 8b8e9ea..b71c580 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -19,7 +19,6 @@
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.content.pm.ParceledListSlice;
-import android.graphics.Bitmap;
 import android.graphics.Region;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -157,9 +156,5 @@
         return -1;
     }
 
-    public Bitmap takeScreenshot(int displayId) {
-        return null;
-    }
-
-    public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
+    public void takeScreenshot(int displayId, RemoteCallback callback) {}
 }
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/graphics/java/android/graphics/GraphicsStatsService.java
similarity index 85%
rename from services/core/java/com/android/server/GraphicsStatsService.java
rename to graphics/java/android/graphics/GraphicsStatsService.java
index 5179fa7..8dfd6ee 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/graphics/java/android/graphics/GraphicsStatsService.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package android.graphics;
 
+import android.annotation.SystemApi;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -26,13 +27,14 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.MemoryFile;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SharedMemory;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.system.ErrnoException;
 import android.util.Log;
 import android.view.IGraphicsStats;
 import android.view.IGraphicsStatsCallback;
@@ -45,6 +47,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -84,8 +87,8 @@
 
     // This isn't static because we need this to happen after registerNativeMethods, however
     // the class is loaded (and thus static ctor happens) before that occurs.
-    private final int ASHMEM_SIZE = nGetAshmemSize();
-    private final byte[] ZERO_DATA = new byte[ASHMEM_SIZE];
+    private final int mAshmemSize = nGetAshmemSize();
+    private final byte[] mZeroData = new byte[mAshmemSize];
 
     private final Context mContext;
     private final AppOpsManager mAppOps;
@@ -97,6 +100,7 @@
     private Handler mWriteOutHandler;
     private boolean mRotateIsScheduled = false;
 
+    @SystemApi
     public GraphicsStatsService(Context context) {
         mContext = context;
         mAppOps = context.getSystemService(AppOpsManager.class);
@@ -108,7 +112,8 @@
             throw new IllegalStateException("Graphics stats directory does not exist: "
                     + mGraphicsStatsDir.getAbsolutePath());
         }
-        HandlerThread bgthread = new HandlerThread("GraphicsStats-disk", Process.THREAD_PRIORITY_BACKGROUND);
+        HandlerThread bgthread = new HandlerThread("GraphicsStats-disk",
+                Process.THREAD_PRIORITY_BACKGROUND);
         bgthread.start();
 
         mWriteOutHandler = new Handler(bgthread.getLooper(), new Handler.Callback() {
@@ -159,7 +164,7 @@
                 active.mCallback.onRotateGraphicsStatsBuffer();
             } catch (RemoteException e) {
                 Log.w(TAG, String.format("Failed to notify '%s' (pid=%d) to rotate buffers",
-                        active.mInfo.packageName, active.mPid), e);
+                        active.mInfo.mPackageName, active.mPid), e);
             }
         }
         // Give a few seconds for everyone to rotate before doing the cleanup
@@ -167,8 +172,8 @@
     }
 
     @Override
-    public ParcelFileDescriptor requestBufferForProcess(String packageName, IGraphicsStatsCallback token)
-            throws RemoteException {
+    public ParcelFileDescriptor requestBufferForProcess(String packageName,
+            IGraphicsStatsCallback token) throws RemoteException {
         int uid = Binder.getCallingUid();
         int pid = Binder.getCallingPid();
         ParcelFileDescriptor pfd = null;
@@ -196,7 +201,7 @@
     // current day.
     // This method is invoked from native code only.
     @SuppressWarnings({"UnusedDeclaration"})
-    private long pullGraphicsStats(boolean lastFullDay) throws RemoteException {
+    private void pullGraphicsStats(boolean lastFullDay, long pulledData) throws RemoteException {
         int uid = Binder.getCallingUid();
 
         // DUMP and PACKAGE_USAGE_STATS permissions are required to invoke this method.
@@ -213,13 +218,13 @@
 
         long callingIdentity = Binder.clearCallingIdentity();
         try {
-            return pullGraphicsStatsImpl(lastFullDay);
+            pullGraphicsStatsImpl(lastFullDay, pulledData);
         } finally {
             Binder.restoreCallingIdentity(callingIdentity);
         }
     }
 
-    private long pullGraphicsStatsImpl(boolean lastFullDay) {
+    private void pullGraphicsStatsImpl(boolean lastFullDay, long pulledData) {
         long targetDay;
         if (lastFullDay) {
             // Get stats from yesterday. Stats stay constant, because the day is over.
@@ -235,7 +240,7 @@
             buffers = new ArrayList<>(mActive.size());
             for (int i = 0; i < mActive.size(); i++) {
                 ActiveBuffer buffer = mActive.get(i);
-                if (buffer.mInfo.startTime == targetDay) {
+                if (buffer.mInfo.mStartTime == targetDay) {
                     try {
                         buffers.add(new HistoricalBuffer(buffer));
                     } catch (IOException ex) {
@@ -267,18 +272,7 @@
                 }
             }
         } finally {
-            return nFinishDumpInMemory(dump);
-        }
-    }
-
-    private ParcelFileDescriptor getPfd(MemoryFile file) {
-        try {
-            if (!file.getFileDescriptor().valid()) {
-                throw new IllegalStateException("Invalid file descriptor");
-            }
-            return ParcelFileDescriptor.dup(file.getFileDescriptor());
-        } catch (IOException ex) {
-            throw new IllegalStateException("Failed to get PFD from memory file", ex);
+            nFinishDumpInMemory(dump, pulledData, lastFullDay);
         }
     }
 
@@ -286,7 +280,7 @@
             int uid, int pid, String packageName, long versionCode) throws RemoteException {
         ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName, versionCode);
         scheduleRotateLocked();
-        return getPfd(buffer.mProcessBuffer);
+        return buffer.getPfd();
     }
 
     private Calendar normalizeDate(long timestamp) {
@@ -301,13 +295,15 @@
 
     private File pathForApp(BufferInfo info) {
         String subPath = String.format("%d/%s/%d/total",
-                normalizeDate(info.startTime).getTimeInMillis(), info.packageName, info.versionCode);
+                normalizeDate(info.mStartTime).getTimeInMillis(), info.mPackageName,
+                info.mVersionCode);
         return new File(mGraphicsStatsDir, subPath);
     }
 
     private void saveBuffer(HistoricalBuffer buffer) {
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "saving graphicsstats for " + buffer.mInfo.packageName);
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
+                    "saving graphicsstats for " + buffer.mInfo.mPackageName);
         }
         synchronized (mFileAccessLock) {
             File path = pathForApp(buffer.mInfo);
@@ -317,8 +313,9 @@
                 Log.w(TAG, "Unable to create path: '" + parent.getAbsolutePath() + "'");
                 return;
             }
-            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.packageName, buffer.mInfo.versionCode,
-                    buffer.mInfo.startTime, buffer.mInfo.endTime, buffer.mData);
+            nSaveBuffer(path.getAbsolutePath(), buffer.mInfo.mPackageName,
+                    buffer.mInfo.mVersionCode, buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
+                    buffer.mData);
         }
         Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
     }
@@ -365,7 +362,7 @@
             HistoricalBuffer data = new HistoricalBuffer(buffer);
             Message.obtain(mWriteOutHandler, SAVE_BUFFER, data).sendToTarget();
         } catch (IOException e) {
-            Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.packageName, e);
+            Log.w(TAG, "Failed to copy graphicsstats from " + buffer.mInfo.mPackageName, e);
         }
         buffer.closeAllBuffers();
     }
@@ -386,7 +383,7 @@
             if (buffer.mPid == pid
                     && buffer.mUid == uid) {
                 // If the buffer is too old we remove it and return a new one
-                if (buffer.mInfo.startTime < today) {
+                if (buffer.mInfo.mStartTime < today) {
                     buffer.binderDied();
                     break;
                 } else {
@@ -410,8 +407,8 @@
             HistoricalBuffer buffer = buffers.get(i);
             File path = pathForApp(buffer.mInfo);
             skipFiles.add(path);
-            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.packageName,
-                    buffer.mInfo.versionCode,  buffer.mInfo.startTime, buffer.mInfo.endTime,
+            nAddToDump(dump, path.getAbsolutePath(), buffer.mInfo.mPackageName,
+                    buffer.mInfo.mVersionCode,  buffer.mInfo.mStartTime, buffer.mInfo.mEndTime,
                     buffer.mData);
         }
         return skipFiles;
@@ -478,20 +475,20 @@
             long versionCode, long startTime, long endTime, byte[] data);
     private static native void nAddToDump(long dump, String path);
     private static native void nFinishDump(long dump);
-    private static native long nFinishDumpInMemory(long dump);
+    private static native void nFinishDumpInMemory(long dump, long pulledData, boolean lastFullDay);
     private static native void nSaveBuffer(String path, String packageName, long versionCode,
             long startTime, long endTime, byte[] data);
 
     private final class BufferInfo {
-        final String packageName;
-        final long versionCode;
-        long startTime;
-        long endTime;
+        final String mPackageName;
+        final long mVersionCode;
+        long mStartTime;
+        long mEndTime;
 
         BufferInfo(String packageName, long versionCode, long startTime) {
-            this.packageName = packageName;
-            this.versionCode = versionCode;
-            this.startTime = startTime;
+            this.mPackageName = packageName;
+            this.mVersionCode = versionCode;
+            this.mStartTime = startTime;
         }
     }
 
@@ -501,7 +498,8 @@
         final int mPid;
         final IGraphicsStatsCallback mCallback;
         final IBinder mToken;
-        MemoryFile mProcessBuffer;
+        SharedMemory mProcessBuffer;
+        ByteBuffer mMapping;
 
         ActiveBuffer(IGraphicsStatsCallback token, int uid, int pid, String packageName,
                 long versionCode)
@@ -512,8 +510,14 @@
             mCallback = token;
             mToken = mCallback.asBinder();
             mToken.linkToDeath(this, 0);
-            mProcessBuffer = new MemoryFile("GFXStats-" + pid, ASHMEM_SIZE);
-            mProcessBuffer.writeBytes(ZERO_DATA, 0, 0, ASHMEM_SIZE);
+            try {
+                mProcessBuffer = SharedMemory.create("GFXStats-" + pid, mAshmemSize);
+                mMapping = mProcessBuffer.mapReadWrite();
+            } catch (ErrnoException ex) {
+                ex.rethrowAsIOException();
+            }
+            mMapping.position(0);
+            mMapping.put(mZeroData, 0, mAshmemSize);
         }
 
         @Override
@@ -523,20 +527,40 @@
         }
 
         void closeAllBuffers() {
+            if (mMapping != null) {
+                SharedMemory.unmap(mMapping);
+                mMapping = null;
+            }
             if (mProcessBuffer != null) {
                 mProcessBuffer.close();
                 mProcessBuffer = null;
             }
         }
+
+        ParcelFileDescriptor getPfd() {
+            try {
+                return mProcessBuffer.getFdDup();
+            } catch (IOException ex) {
+                throw new IllegalStateException("Failed to get PFD from memory file", ex);
+            }
+        }
+
+        void readBytes(byte[] buffer, int count) throws IOException  {
+            if (mMapping == null) {
+                throw new IOException("SharedMemory has been deactivated");
+            }
+            mMapping.position(0);
+            mMapping.get(buffer, 0, count);
+        }
     }
 
     private final class HistoricalBuffer {
         final BufferInfo mInfo;
-        final byte[] mData = new byte[ASHMEM_SIZE];
+        final byte[] mData = new byte[mAshmemSize];
         HistoricalBuffer(ActiveBuffer active) throws IOException {
             mInfo = active.mInfo;
-            mInfo.endTime = System.currentTimeMillis();
-            active.mProcessBuffer.readBytes(mData, 0, 0, ASHMEM_SIZE);
+            mInfo.mEndTime = System.currentTimeMillis();
+            active.readBytes(mData, mAshmemSize);
         }
     }
 }
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 3b86413..d08bfcf 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -157,7 +157,7 @@
     public HardwareRenderer() {
         mRootNode = RenderNode.adopt(nCreateRootRenderNode());
         mRootNode.setClipToBounds(false);
-        mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
+        mNativeProxy = nCreateProxy(!mOpaque, mIsWideGamut, mRootNode.mNativeRenderNode);
         if (mNativeProxy == 0) {
             throw new OutOfMemoryError("Unable to create hardware renderer");
         }
@@ -1085,7 +1085,8 @@
 
     private static native long nCreateRootRenderNode();
 
-    private static native long nCreateProxy(boolean translucent, long rootRenderNode);
+    private static native long nCreateProxy(boolean translucent, boolean isWideGamut,
+            long rootRenderNode);
 
     private static native void nDeleteProxy(long nativeProxy);
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 51270f5..301d1af 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -92,9 +92,12 @@
                 "libandroidfw",
                 "libcrypto",
                 "libsync",
+                "libstatspull",
+                "libstatssocket",
             ],
             static_libs: [
                 "libEGL_blobCache",
+                "libprotoutil",
             ],
         },
         host: {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6761435..31e4555 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -27,7 +27,6 @@
 #include "DamageAccumulator.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #endif
-#include "utils/FatVector.h"
 #include "utils/MathUtils.h"
 #include "utils/StringUtils.h"
 #include "utils/TraceUtils.h"
@@ -37,6 +36,7 @@
 #include <atomic>
 #include <sstream>
 #include <string>
+#include <ui/FatVector.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index d55e5b0..c0ec217 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -27,6 +27,8 @@
 
 #include <androidfw/ResourceTypes.h>
 
+#include <ui/FatVector.h>
+
 #include "AnimatorManager.h"
 #include "CanvasTransform.h"
 #include "Debug.h"
@@ -35,7 +37,6 @@
 #include "RenderProperties.h"
 #include "pipeline/skia/SkiaDisplayList.h"
 #include "pipeline/skia/SkiaLayer.h"
-#include "utils/FatVector.h"
 
 #include <vector>
 
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 4b2857f..afd82ac 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -24,6 +24,19 @@
 
 using namespace android;
 
+sk_sp<SkColorSpace> ImageDecoder::getDefaultColorSpace() const {
+    const skcms_ICCProfile* encodedProfile = mCodec->getICCProfile();
+    if (encodedProfile) {
+        // If the profile maps directly to an SkColorSpace, that SkColorSpace
+        // will be returned. Otherwise, nullptr will be returned. In either
+        // case, using this SkColorSpace results in doing no color correction.
+        return SkColorSpace::Make(*encodedProfile);
+    }
+
+    // The image has no embedded color profile, and should be treated as SRGB.
+    return SkColorSpace::MakeSRGB();
+}
+
 ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
     : mCodec(std::move(codec))
     , mPeeker(std::move(peeker))
@@ -31,7 +44,7 @@
     , mDecodeSize(mTargetSize)
     , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
     , mUnpremultipliedRequired(false)
-    , mOutColorSpace(mCodec->computeOutputColorSpace(mOutColorType, nullptr))
+    , mOutColorSpace(getDefaultColorSpace())
     , mSampleSize(1)
 {
 }
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index 0c99f84..a1b5157 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -43,6 +43,7 @@
 
     bool setUnpremultipliedRequired(bool unpremultipliedRequired);
 
+    sk_sp<SkColorSpace> getDefaultColorSpace() const;
     void setOutColorSpace(sk_sp<SkColorSpace> cs);
 
     // The size is the final size after scaling and cropping.
diff --git a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
index cfc0f9b..d669f84 100644
--- a/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
+++ b/libs/hwui/pipeline/skia/ReorderBarrierDrawables.h
@@ -21,7 +21,7 @@
 
 #include <SkCanvas.h>
 #include <SkDrawable.h>
-#include <utils/FatVector.h>
+#include <ui/FatVector.h>
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index e7efe2f..8d5acc6 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -171,17 +171,15 @@
 }
 
 bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
-                                    ColorMode colorMode, uint32_t extraBuffers) {
+                                    uint32_t extraBuffers) {
     if (mEglSurface != EGL_NO_SURFACE) {
         mEglManager.destroySurface(mEglSurface);
         mEglSurface = EGL_NO_SURFACE;
     }
 
-    setSurfaceColorProperties(colorMode);
-
     if (surface) {
         mRenderThread.requireGlContext();
-        auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorSpace);
+        auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace);
         if (!newSurface) {
             return false;
         }
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index 3fe0f92..e482cad 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -44,7 +44,7 @@
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
     bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
-                    renderthread::ColorMode colorMode, uint32_t extraBuffers) override;
+                    uint32_t extraBuffers) override;
     void onStop() override;
     bool isSurfaceReady() override;
     bool isContextReady() override;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 6f4af3d..29b4dd7 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -44,6 +44,7 @@
 namespace skiapipeline {
 
 SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) {
+    setSurfaceColorProperties(mColorMode);
 }
 
 SkiaPipeline::~SkiaPipeline() {
@@ -584,6 +585,7 @@
 }
 
 void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
+    mColorMode = colorMode;
     if (colorMode == ColorMode::SRGB) {
         mSurfaceColorType = SkColorType::kN32_SkColorType;
         mSurfaceColorSpace = SkColorSpace::MakeSRGB();
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index af8414d..8341164 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -50,6 +50,7 @@
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                              ErrorHandler* errorHandler) override;
 
+    void setSurfaceColorProperties(renderthread::ColorMode colorMode) override;
     SkColorType getSurfaceColorType() const override { return mSurfaceColorType; }
     sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
 
@@ -72,9 +73,10 @@
 
 protected:
     void dumpResourceCacheUsage() const;
-    void setSurfaceColorProperties(renderthread::ColorMode colorMode);
 
     renderthread::RenderThread& mRenderThread;
+
+    renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB;
     SkColorType mSurfaceColorType;
     sk_sp<SkColorSpace> mSurfaceColorSpace;
 
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index ad7c706..535a199 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -117,17 +117,16 @@
 void SkiaVulkanPipeline::onStop() {}
 
 bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior,
-                                    ColorMode colorMode, uint32_t extraBuffers) {
+                                    uint32_t extraBuffers) {
     if (mVkSurface) {
         mVkManager.destroySurface(mVkSurface);
         mVkSurface = nullptr;
     }
 
-    setSurfaceColorProperties(colorMode);
     if (surface) {
         mRenderThread.requireVkContext();
         mVkSurface =
-                mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, mSurfaceColorType,
+                mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType,
                                          mRenderThread.getGrContext(), extraBuffers);
     }
 
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
index 3173478..c8bf233 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h
@@ -43,7 +43,7 @@
                      FrameInfo* currentFrameInfo, bool* requireSwap) override;
     DeferredLayerUpdater* createTextureLayer() override;
     bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior,
-                    renderthread::ColorMode colorMode, uint32_t extraBuffers) override;
+                    uint32_t extraBuffers) override;
     void onStop() override;
     bool isSurfaceReady() override;
     bool isContextReady() override;
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index aa8bac9..91f9447 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -161,9 +161,8 @@
         mRenderAheadCapacity = mRenderAheadDepth;
     }
 
-    ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
     bool hasSurface = mRenderPipeline->setSurface(
-            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode,
+            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior,
             mRenderAheadCapacity);
 
     mFrameNumber = -1;
@@ -225,7 +224,8 @@
 }
 
 void CanvasContext::setWideGamut(bool wideGamut) {
-    mWideColorGamut = wideGamut;
+    ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
+    mRenderPipeline->setSurfaceColorProperties(colorMode);
 }
 
 bool CanvasContext::makeCurrent() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0967b20..629c741 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -251,7 +251,6 @@
     nsecs_t mLastDropVsync = 0;
 
     bool mOpaque;
-    bool mWideColorGamut = false;
     bool mUseForceDark = false;
     LightInfo mLightInfo;
     LightGeometry mLightGeometry = {{0, 0, 0}, 0};
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index ef0aa98..ba0d64c 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -66,7 +66,7 @@
     virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty,
                              FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
     virtual DeferredLayerUpdater* createTextureLayer() = 0;
-    virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode,
+    virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior,
                             uint32_t extraBuffers) = 0;
     virtual void onStop() = 0;
     virtual bool isSurfaceReady() = 0;
@@ -80,6 +80,8 @@
     virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
     virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
     virtual void unpinImages() = 0;
+
+    virtual void setSurfaceColorProperties(ColorMode colorMode) = 0;
     virtual SkColorType getSurfaceColorType() const = 0;
     virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0;
     virtual GrSurfaceOrigin getSurfaceOrigin() = 0;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index cae3e3b..206b58f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -27,7 +27,6 @@
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaVulkanPipeline.h"
 #include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
 #include "utils/TimeUtils.h"
 #include "utils/TraceUtils.h"
 
@@ -40,6 +39,8 @@
 #include <utils/Mutex.h>
 #include <thread>
 
+#include <ui/FatVector.h>
+
 namespace android {
 namespace uirenderer {
 namespace renderthread {
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a5355fc..ba70afc 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -23,13 +23,13 @@
 #include <GrContext.h>
 #include <GrTypes.h>
 #include <android/sync.h>
+#include <ui/FatVector.h>
 #include <vk/GrVkExtensions.h>
 #include <vk/GrVkTypes.h>
 
 #include "Properties.h"
 #include "RenderThread.h"
 #include "renderstate/RenderState.h"
-#include "utils/FatVector.h"
 #include "utils/TraceUtils.h"
 
 namespace android {
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index c418617..644d5fb 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -26,9 +26,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <algorithm>
-#include <map>
-#include <vector>
+#include <android/util/ProtoOutputStream.h>
+#include <stats_event.h>
+#include <statslog.h>
 
 #include "JankTracker.h"
 #include "protos/graphicsstats.pb.h"
@@ -61,7 +61,7 @@
         }
     }
     bool valid() { return mFd != -1; }
-    operator int() { return mFd; }  // NOLINT(google-explicit-constructor)
+    operator int() { return mFd; } // NOLINT(google-explicit-constructor)
 
 private:
     int mFd;
@@ -485,79 +485,82 @@
     delete dump;
 }
 
-class MemOutputStreamLite : public io::ZeroCopyOutputStream {
-public:
-    explicit MemOutputStreamLite() : mCopyAdapter(), mImpl(&mCopyAdapter) {}
-    virtual ~MemOutputStreamLite() {}
+using namespace google::protobuf;
 
-    virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
+// Field ids taken from FrameTimingHistogram message in atoms.proto
+#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
+#define FRAME_COUNTS_FIELD_NUMBER 2
 
-    virtual void BackUp(int count) override { mImpl.BackUp(count); }
-
-    virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
-
-    bool Flush() { return mImpl.Flush(); }
-
-    void copyData(const DumpMemoryFn& reader, void* param1, void* param2) {
-        int bufferOffset = 0;
-        int totalSize = mCopyAdapter.mBuffersSize - mCopyAdapter.mCurrentBufferUnusedSize;
-        int totalDataLeft = totalSize;
-        for (auto& it : mCopyAdapter.mBuffers) {
-            int bufferSize = std::min(totalDataLeft, (int)it.size());  // last buffer is not full
-            reader(it.data(), bufferOffset, bufferSize, totalSize, param1, param2);
-            bufferOffset += bufferSize;
-            totalDataLeft -= bufferSize;
-        }
+static void writeCpuHistogram(AStatsEvent* event,
+                              const uirenderer::protos::GraphicsStatsProto& stat) {
+    util::ProtoOutputStream proto;
+    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+        auto& bucket = stat.histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+                    (int)bucket.render_millis());
     }
-
-private:
-    struct MemAdapter : public io::CopyingOutputStream {
-        // Data is stored in an array of buffers.
-        // JNI SetByteArrayRegion assembles data in one continuous Java byte[] buffer.
-        std::vector<std::vector<unsigned char>> mBuffers;
-        int mBuffersSize = 0;                     // total bytes allocated in mBuffers
-        int mCurrentBufferUnusedSize = 0;         // unused bytes in the last buffer mBuffers.back()
-        unsigned char* mCurrentBuffer = nullptr;  // pointer to next free byte in mBuffers.back()
-
-        explicit MemAdapter() {}
-        virtual ~MemAdapter() {}
-
-        virtual bool Write(const void* buffer, int size) override {
-            while (size > 0) {
-                if (0 == mCurrentBufferUnusedSize) {
-                    mCurrentBufferUnusedSize =
-                            std::max(size, mBuffersSize ? 2 * mBuffersSize : 10000);
-                    mBuffers.emplace_back();
-                    mBuffers.back().resize(mCurrentBufferUnusedSize);
-                    mCurrentBuffer = mBuffers.back().data();
-                    mBuffersSize += mCurrentBufferUnusedSize;
-                }
-                int dataMoved = std::min(mCurrentBufferUnusedSize, size);
-                memcpy(mCurrentBuffer, buffer, dataMoved);
-                mCurrentBufferUnusedSize -= dataMoved;
-                mCurrentBuffer += dataMoved;
-                buffer = reinterpret_cast<const unsigned char*>(buffer) + dataMoved;
-                size -= dataMoved;
-            }
-            return true;
-        }
-    };
-
-    MemOutputStreamLite::MemAdapter mCopyAdapter;
-    io::CopyingOutputStreamAdaptor mImpl;
-};
-
-void GraphicsStatsService::finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
-                                              void* param2) {
-    MemOutputStreamLite stream;
-    dump->updateProto();
-    bool success = dump->proto().SerializeToZeroCopyStream(&stream) && stream.Flush();
-    delete dump;
-    if (!success) {
-        return;
+    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
+        auto& bucket = stat.histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
+                    (long long)bucket.frame_count());
     }
-    stream.copyData(reader, param1, param2);
+    std::vector<uint8_t> outVector;
+    proto.serializeToVector(&outVector);
+    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
 }
 
+static void writeGpuHistogram(AStatsEvent* event,
+                              const uirenderer::protos::GraphicsStatsProto& stat) {
+    util::ProtoOutputStream proto;
+    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+        auto& bucket = stat.gpu_histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
+                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
+                    (int)bucket.render_millis());
+    }
+    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
+        auto& bucket = stat.gpu_histogram(bucketIndex);
+        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
+                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
+                    (long long)bucket.frame_count());
+    }
+    std::vector<uint8_t> outVector;
+    proto.serializeToVector(&outVector);
+    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
+}
+
+
+void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data,
+                                              bool lastFullDay) {
+    dump->updateProto();
+    auto& serviceDump = dump->proto();
+    for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
+        auto& stat = serviceDump.stats(stat_index);
+        AStatsEvent* event = AStatsEventList_addStatsEvent(data);
+        AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
+        AStatsEvent_writeString(event, stat.package_name().c_str());
+        AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
+        AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
+        AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
+        AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
+        AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
+        writeCpuHistogram(event, stat);
+        writeGpuHistogram(event, stat);
+        // TODO: fill in UI mainline module version, when the feature is available.
+        AStatsEvent_writeInt64(event, (int64_t)0);
+        AStatsEvent_writeBool(event, !lastFullDay);
+        AStatsEvent_build(event);
+    }
+}
+
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/service/GraphicsStatsService.h b/libs/hwui/service/GraphicsStatsService.h
index 4bed9633..59e21d0 100644
--- a/libs/hwui/service/GraphicsStatsService.h
+++ b/libs/hwui/service/GraphicsStatsService.h
@@ -20,6 +20,7 @@
 
 #include "JankTracker.h"
 #include "utils/Macros.h"
+#include <stats_pull_atom_callback.h>
 
 namespace android {
 namespace uirenderer {
@@ -27,9 +28,6 @@
 class GraphicsStatsProto;
 }
 
-typedef void (*DumpMemoryFn)(void* buffer, int bufferOffset, int bufferSize, int totalSize,
-                             void* param1, void* param2);
-
 /*
  * The exported entry points used by GraphicsStatsService.java in f/b/services/core
  *
@@ -56,8 +54,8 @@
                                       int64_t startTime, int64_t endTime, const ProfileData* data);
     ANDROID_API static void addToDump(Dump* dump, const std::string& path);
     ANDROID_API static void finishDump(Dump* dump);
-    ANDROID_API static void finishDumpInMemory(Dump* dump, const DumpMemoryFn& reader, void* param1,
-                                               void* param2);
+    ANDROID_API static void finishDumpInMemory(Dump* dump, AStatsEventList* data,
+                                               bool lastFullDay);
 
     // Visible for testing
     static bool parseFromFile(const std::string& path, protos::GraphicsStatsProto* output);
diff --git a/libs/hwui/tests/unit/FatVectorTests.cpp b/libs/hwui/tests/unit/FatVectorTests.cpp
index 8523e6c..6585a62 100644
--- a/libs/hwui/tests/unit/FatVectorTests.cpp
+++ b/libs/hwui/tests/unit/FatVectorTests.cpp
@@ -15,7 +15,7 @@
  */
 
 #include <gtest/gtest.h>
-#include <utils/FatVector.h>
+#include <ui/FatVector.h>
 
 #include <tests/common/TestUtils.h>
 
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 307d136..90bcd1c 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -398,7 +398,7 @@
     auto surface = context.surface();
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
     EXPECT_FALSE(pipeline->isSurfaceReady());
-    EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, ColorMode::SRGB, 0));
+    EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, 0));
     EXPECT_TRUE(pipeline->isSurfaceReady());
     renderThread.destroyRenderingContext();
     EXPECT_FALSE(pipeline->isSurfaceReady());
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
deleted file mode 100644
index 8cc4d10..0000000
--- a/libs/hwui/utils/FatVector.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2015, The Android Open Source Project
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef ANDROID_FAT_VECTOR_H
-#define ANDROID_FAT_VECTOR_H
-
-#include "utils/Macros.h"
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <utils/Log.h>
-#include <type_traits>
-
-#include <vector>
-
-namespace android {
-namespace uirenderer {
-
-template <typename T, size_t SIZE>
-class InlineStdAllocator {
-public:
-    struct Allocation {
-        PREVENT_COPY_AND_ASSIGN(Allocation);
-
-    public:
-        Allocation(){};
-        // char array instead of T array, so memory is uninitialized, with no destructors run
-        char array[sizeof(T) * SIZE];
-        bool inUse = false;
-    };
-
-    typedef T value_type;  // needed to implement std::allocator
-    typedef T* pointer;    // needed to implement std::allocator
-
-    explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
-    InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
-    ~InlineStdAllocator() {}
-
-    T* allocate(size_t num, const void* = 0) {
-        if (!mAllocation.inUse && num <= SIZE) {
-            mAllocation.inUse = true;
-            return (T*)mAllocation.array;
-        } else {
-            return (T*)malloc(num * sizeof(T));
-        }
-    }
-
-    void deallocate(pointer p, size_t num) {
-        if (p == (T*)mAllocation.array) {
-            mAllocation.inUse = false;
-        } else {
-            // 'free' instead of delete here - destruction handled separately
-            free(p);
-        }
-    }
-    Allocation& mAllocation;
-};
-
-/**
- * std::vector with SIZE elements preallocated into an internal buffer.
- *
- * Useful for avoiding the cost of malloc in cases where only SIZE or
- * fewer elements are needed in the common case.
- */
-template <typename T, size_t SIZE>
-class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
-public:
-    FatVector()
-            : std::vector<T, InlineStdAllocator<T, SIZE>>(
-                      InlineStdAllocator<T, SIZE>(mAllocation)) {
-        this->reserve(SIZE);
-    }
-
-    explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
-
-private:
-    typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
-};
-
-}  // namespace uirenderer
-}  // namespace android
-
-#endif  // ANDROID_FAT_VECTOR_H
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index e4348f2..3b494e9 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -251,19 +251,24 @@
 void PointerController::setPresentation(Presentation presentation) {
     AutoMutex _l(mLock);
 
-    if (presentation == PRESENTATION_POINTER && mLocked.additionalMouseResources.empty()) {
-        mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
-                &mLocked.animationResources, mLocked.viewport.displayId);
+    if (mLocked.presentation == presentation) {
+        return;
     }
 
-    if (mLocked.presentation != presentation) {
-        mLocked.presentation = presentation;
-        mLocked.presentationChanged = true;
+    mLocked.presentation = presentation;
+    mLocked.presentationChanged = true;
 
-        if (presentation != PRESENTATION_SPOT) {
-            fadeOutAndReleaseAllSpotsLocked();
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
+    if (presentation == PRESENTATION_POINTER) {
+        if (mLocked.additionalMouseResources.empty()) {
+            mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+                                                  &mLocked.animationResources,
+                                                  mLocked.viewport.displayId);
         }
-
+        fadeOutAndReleaseAllSpotsLocked();
         updatePointerLocked();
     }
 }
@@ -285,6 +290,9 @@
 #endif
 
     AutoMutex _l(mLock);
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
 
     std::vector<Spot*> newSpots;
     std::map<int32_t, std::vector<Spot*>>::const_iterator iter =
@@ -331,6 +339,9 @@
 #endif
 
     AutoMutex _l(mLock);
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
 
     fadeOutAndReleaseAllSpotsLocked();
 }
@@ -752,6 +763,10 @@
 }
 
 void PointerController::loadResourcesLocked() REQUIRES(mLock) {
+    if (!mLocked.viewport.isValid()) {
+        return;
+    }
+
     mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId);
     mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId);
 
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index b36406d..a157426 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -39,8 +39,8 @@
 
 using ::testing::AllOf;
 using ::testing::Field;
-using ::testing::NiceMock;
 using ::testing::Mock;
+using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::Test;
 
@@ -57,12 +57,20 @@
     virtual int32_t getDefaultPointerIconId() override;
     virtual int32_t getCustomPointerIconId() override;
 
+    bool allResourcesAreLoaded();
+    bool noResourcesAreLoaded();
+
 private:
     void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+
+    bool pointerIconLoaded{false};
+    bool pointerResourcesLoaded{false};
+    bool additionalMouseResourcesLoaded{false};
 };
 
 void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
     loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+    pointerIconLoaded = true;
 }
 
 void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
@@ -70,6 +78,7 @@
     loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
     loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
     loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+    pointerResourcesLoaded = true;
 }
 
 void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
@@ -91,6 +100,8 @@
     anim.durationPerFrame = 10;
     (*outResources)[cursorType] = icon;
     (*outAnimationResources)[cursorType] = anim;
+
+    additionalMouseResourcesLoaded = true;
 }
 
 int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
@@ -101,18 +112,27 @@
     return CURSOR_TYPE_CUSTOM;
 }
 
+bool MockPointerControllerPolicyInterface::allResourcesAreLoaded() {
+    return pointerIconLoaded && pointerResourcesLoaded && additionalMouseResourcesLoaded;
+}
+
+bool MockPointerControllerPolicyInterface::noResourcesAreLoaded() {
+    return !(pointerIconLoaded || pointerResourcesLoaded || additionalMouseResourcesLoaded);
+}
+
 void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
     icon->style = type;
     std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
     icon->hotSpotX = hotSpot.first;
     icon->hotSpotY = hotSpot.second;
 }
-
 class PointerControllerTest : public Test {
 protected:
     PointerControllerTest();
     ~PointerControllerTest();
 
+    void ensureDisplayViewportIsSet();
+
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
     sp<MockSpriteController> mSpriteController;
@@ -141,7 +161,14 @@
             .WillOnce(Return(mPointerSprite));
 
     mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+}
 
+PointerControllerTest::~PointerControllerTest() {
+    mRunning.store(false, std::memory_order_relaxed);
+    mThread.join();
+}
+
+void PointerControllerTest::ensureDisplayViewportIsSet() {
     DisplayViewport viewport;
     viewport.displayId = ADISPLAY_ID_DEFAULT;
     viewport.logicalRight = 1600;
@@ -151,11 +178,9 @@
     viewport.deviceWidth = 400;
     viewport.deviceHeight = 300;
     mPointerController->setDisplayViewport(viewport);
-}
 
-PointerControllerTest::~PointerControllerTest() {
-    mRunning.store(false, std::memory_order_relaxed);
-    mThread.join();
+    // The first call to setDisplayViewport should trigger the loading of the necessary resources.
+    EXPECT_TRUE(mPolicy->allResourcesAreLoaded());
 }
 
 void PointerControllerTest::loopThread() {
@@ -167,6 +192,7 @@
 }
 
 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+    ensureDisplayViewportIsSet();
     mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
 
     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
@@ -181,6 +207,7 @@
 }
 
 TEST_F(PointerControllerTest, updatePointerIcon) {
+    ensureDisplayViewportIsSet();
     mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
 
     int32_t type = CURSOR_TYPE_ADDITIONAL;
@@ -196,6 +223,7 @@
 }
 
 TEST_F(PointerControllerTest, setCustomPointerIcon) {
+    ensureDisplayViewportIsSet();
     mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
 
     int32_t style = CURSOR_TYPE_CUSTOM;
@@ -217,4 +245,18 @@
     mPointerController->setCustomPointerIcon(icon);
 }
 
+TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
+    mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+    mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
+    mPointerController->clearSpots();
+    mPointerController->setPosition(1.0f, 1.0f);
+    mPointerController->move(1.0f, 1.0f);
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+
+    EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
+
+    ensureDisplayViewportIsSet();
+}
+
 }  // namespace android
diff --git a/libs/services/Android.bp b/libs/services/Android.bp
index 9b047ca..1e62107 100644
--- a/libs/services/Android.bp
+++ b/libs/services/Android.bp
@@ -20,7 +20,6 @@
         ":IDropBoxManagerService.aidl",
         "src/content/ComponentName.cpp",
         "src/os/DropBoxManager.cpp",
-        "src/os/StatsDimensionsValue.cpp",
     ],
 
     shared_libs: [
diff --git a/libs/services/include/android/os/StatsDimensionsValue.h b/libs/services/include/android/os/StatsDimensionsValue.h
deleted file mode 100644
index cc0b056..0000000
--- a/libs/services/include/android/os/StatsDimensionsValue.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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.
- */
-#ifndef STATS_DIMENSIONS_VALUE_H
-#define STATS_DIMENSIONS_VALUE_H
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <binder/Status.h>
-#include <utils/String16.h>
-#include <vector>
-
-namespace android {
-namespace os {
-
-// Represents a parcelable object. Used to send data from statsd to StatsCompanionService.java.
-class StatsDimensionsValue : public android::Parcelable {
-public:
-    StatsDimensionsValue();
-
-    StatsDimensionsValue(int32_t field, String16 value);
-    StatsDimensionsValue(int32_t field, int32_t value);
-    StatsDimensionsValue(int32_t field, int64_t value);
-    StatsDimensionsValue(int32_t field, bool value);
-    StatsDimensionsValue(int32_t field, float value);
-    StatsDimensionsValue(int32_t field, std::vector<StatsDimensionsValue> value);
-
-    virtual ~StatsDimensionsValue();
-
-    virtual android::status_t writeToParcel(android::Parcel* out) const override;
-    virtual android::status_t readFromParcel(const android::Parcel* in) override;
-
-private:
-    // Keep constants in sync with android/os/StatsDimensionsValue.java
-    // and stats_log.proto's DimensionValue.
-    static const int kStrValueType = 2;
-    static const int kIntValueType = 3;
-    static const int kLongValueType = 4;
-    static const int kBoolValueType = 5;
-    static const int kFloatValueType = 6;
-    static const int kTupleValueType = 7;
-
-    int32_t mField;
-    int32_t mValueType;
-
-    // This isn't very clever, but it isn't used for long-term storage, so it'll do.
-    String16 mStrValue;
-    int32_t mIntValue;
-    int64_t mLongValue;
-    bool mBoolValue;
-    float mFloatValue;
-    std::vector<StatsDimensionsValue> mTupleValue;
-};
-
-}  // namespace os
-}  // namespace android
-
-#endif // STATS_DIMENSIONS_VALUE_H
diff --git a/libs/services/src/os/StatsDimensionsValue.cpp b/libs/services/src/os/StatsDimensionsValue.cpp
deleted file mode 100644
index 0052e0b..0000000
--- a/libs/services/src/os/StatsDimensionsValue.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.
- */
-
-#define LOG_TAG "StatsDimensionsValue"
-
-#include "android/os/StatsDimensionsValue.h"
-
-#include <cutils/log.h>
-
-using android::Parcel;
-using android::Parcelable;
-using android::status_t;
-using std::vector;
-
-namespace android {
-namespace os {
-
-StatsDimensionsValue::StatsDimensionsValue() {};
-
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, String16 value) :
-    mField(field),
-    mValueType(kStrValueType),
-    mStrValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, int32_t value) :
-    mField(field),
-    mValueType(kIntValueType),
-    mIntValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, int64_t value) :
-    mField(field),
-    mValueType(kLongValueType),
-    mLongValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, bool value) :
-    mField(field),
-    mValueType(kBoolValueType),
-    mBoolValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, float value) :
-    mField(field),
-    mValueType(kFloatValueType),
-    mFloatValue(value) {
-}
-StatsDimensionsValue::StatsDimensionsValue(int32_t field, vector<StatsDimensionsValue> value) :
-    mField(field),
-    mValueType(kTupleValueType),
-    mTupleValue(value) {
-}
-
-StatsDimensionsValue::~StatsDimensionsValue() {}
-
-status_t
-StatsDimensionsValue::writeToParcel(Parcel* out) const {
-    status_t err ;
-
-    err = out->writeInt32(mField);
-    if (err != NO_ERROR) {
-        return err;
-    }
-    err = out->writeInt32(mValueType);
-    if (err != NO_ERROR) {
-        return err;
-    }
-    switch (mValueType) {
-        case kStrValueType:
-            err = out->writeString16(mStrValue);
-            break;
-        case kIntValueType:
-            err = out->writeInt32(mIntValue);
-            break;
-        case kLongValueType:
-            err = out->writeInt64(mLongValue);
-            break;
-        case kBoolValueType:
-            err = out->writeBool(mBoolValue);
-            break;
-        case kFloatValueType:
-            err = out->writeFloat(mFloatValue);
-            break;
-        case kTupleValueType:
-            {
-                int sz = mTupleValue.size();
-                err = out->writeInt32(sz);
-                if (err != NO_ERROR) {
-                    return err;
-                }
-                for (int i = 0; i < sz; ++i) {
-                    err = mTupleValue[i].writeToParcel(out);
-                    if (err != NO_ERROR) {
-                        return err;
-                    }
-                }
-            }
-            break;
-        default:
-            err = UNKNOWN_ERROR;
-            break;
-    }
-    return err;
-}
-
-status_t
-StatsDimensionsValue::readFromParcel(const Parcel* in)
-{
-    // Implement me if desired. We don't currently use this.
-    ALOGE("Cannot do c++ StatsDimensionsValue.readFromParcel(); it is not implemented.");
-    (void)in; // To prevent compile error of unused parameter 'in'
-    return UNKNOWN_ERROR;
-}
-
-}  // namespace os
-}  // namespace android
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/location/java/android/location/GnssRequest.aidl
similarity index 76%
copy from core/java/android/os/StatsDimensionsValue.aidl
copy to location/java/android/location/GnssRequest.aidl
index 81a14a4..581abcc 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/location/java/android/location/GnssRequest.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
+/*
+ * Copyright (C) 2020, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-package android.os;
+package android.location;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+/**
+ * @hide
+ */
+parcelable GnssRequest;
diff --git a/location/java/android/location/GnssRequest.java b/location/java/android/location/GnssRequest.java
new file mode 100644
index 0000000..2afb265
--- /dev/null
+++ b/location/java/android/location/GnssRequest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.location;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class contains extra parameters to pass to a GNSS provider implementation.
+ * @hide
+ */
+@SystemApi
+public final class GnssRequest implements Parcelable {
+    private final boolean mFullTracking;
+
+    /**
+     * Creates a {@link GnssRequest} with a full list of parameters.
+     */
+    private GnssRequest(boolean fullTracking) {
+        mFullTracking = fullTracking;
+    }
+
+    /**
+     * Represents whether to enable full GNSS tracking.
+     *
+     * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
+     * discontinuities are expected, and when supported, carrier phase should be continuous in
+     * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+     * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
+     * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
+     * duty cycling, constellations and frequency limits, etc.
+     */
+    public boolean isFullTracking() {
+        return mFullTracking;
+    }
+
+    @NonNull
+    public static final Creator<GnssRequest> CREATOR =
+            new Creator<GnssRequest>() {
+                @Override
+                @NonNull
+                public GnssRequest createFromParcel(@NonNull Parcel parcel) {
+                    return new GnssRequest(parcel.readBoolean());
+                }
+
+                @Override
+                public GnssRequest[] newArray(int i) {
+                    return new GnssRequest[i];
+                }
+            };
+
+    @NonNull
+    @Override
+    public String toString() {
+        StringBuilder s = new StringBuilder();
+        s.append("GnssRequest[");
+        s.append("FullTracking=").append(mFullTracking);
+        s.append(']');
+        return s.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null) return false;
+        if (!(obj instanceof GnssRequest)) return false;
+
+        GnssRequest other = (GnssRequest) obj;
+        if (mFullTracking != other.mFullTracking) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        return mFullTracking ? 1 : 0;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeBoolean(mFullTracking);
+    }
+
+    /** Builder for {@link GnssRequest} */
+    public static final class Builder {
+        private boolean mFullTracking;
+
+        /**
+         * Constructs a {@link Builder} instance.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a {@link Builder} instance by copying a {@link GnssRequest}.
+         */
+        public Builder(@NonNull GnssRequest request) {
+            mFullTracking = request.isFullTracking();
+        }
+
+        /**
+         * Set the value of whether to enable full GNSS tracking, which is false by default.
+         *
+         * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
+         * discontinuities are expected, and when supported, carrier phase should be continuous in
+         * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+         * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
+         * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
+         * duty cycling, constellations and frequency limits, etc.
+         *
+         * <p>Full tracking requests always override non-full tracking requests. If any full
+         * tracking request occurs, all listeners on the device will receive full tracking GNSS
+         * measurements.
+         */
+        @NonNull public Builder setFullTracking(boolean value) {
+            mFullTracking = value;
+            return this;
+        }
+
+        /** Builds a {@link GnssRequest} instance as specified by this builder. */
+        @NonNull
+        public GnssRequest build() {
+            return new GnssRequest(mFullTracking);
+        }
+    }
+}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 197787e..7e6486c 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -33,6 +33,9 @@
 import android.annotation.TestApi;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -50,7 +53,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.ArrayMap;
-import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ProviderProperties;
@@ -82,6 +84,36 @@
     private static final String TAG = "LocationManager";
 
     /**
+     * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
+     * specific package.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
+    public static final long TARGETED_PENDING_INTENT = 148963590L;
+
+    /**
+     * For apps targeting Android K and above, incomplete locations may not be passed to
+     * {@link #setTestProviderLocation}.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
+    private static final long INCOMPLETE_LOCATION = 148964793L;
+
+    /**
+     * For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with
+     * {@link GnssStatus} APIs.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+    private static final long GPS_STATUS_USAGE = 144027538L;
+
+    /**
      * Name of the network location provider.
      *
      * <p>This provider determines location based on nearby of cell tower and WiFi access points.
@@ -771,7 +803,6 @@
     public void requestSingleUpdate(@NonNull String provider,
             @NonNull PendingIntent pendingIntent) {
         Preconditions.checkArgument(provider != null, "invalid null provider");
-        checkPendingIntent(pendingIntent);
 
         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                 provider, 0, 0, true);
@@ -800,7 +831,6 @@
     public void requestSingleUpdate(@NonNull Criteria criteria,
             @NonNull PendingIntent pendingIntent) {
         Preconditions.checkArgument(criteria != null, "invalid null criteria");
-        checkPendingIntent(pendingIntent);
 
         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
                 criteria, 0, 0, true);
@@ -1021,7 +1051,6 @@
     public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
             @NonNull PendingIntent pendingIntent) {
         Preconditions.checkArgument(provider != null, "invalid null provider");
-        checkPendingIntent(pendingIntent);
 
         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                 provider, minTimeMs, minDistanceM, false);
@@ -1048,7 +1077,6 @@
     public void requestLocationUpdates(long minTimeMs, float minDistanceM,
             @NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) {
         Preconditions.checkArgument(criteria != null, "invalid null criteria");
-        checkPendingIntent(pendingIntent);
 
         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
                 criteria, minTimeMs, minDistanceM, false);
@@ -1164,9 +1192,9 @@
             @NonNull PendingIntent pendingIntent) {
         Preconditions.checkArgument(locationRequest != null, "invalid null location request");
         Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
-        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
+        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
             Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
-                    "pending intent must be targeted to package");
+                    "pending intent must be targeted to a package");
         }
 
         try {
@@ -1198,15 +1226,9 @@
      */
     @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
     public boolean injectLocation(@NonNull Location location) {
-        if (location == null) {
-            IllegalArgumentException e = new IllegalArgumentException("invalid null location");
-            if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
-                throw e;
-            } else {
-                Log.w(TAG, e);
-                return false;
-            }
-        }
+        Preconditions.checkArgument(location != null, "invalid null location");
+        Preconditions.checkArgument(location.isComplete(),
+                "incomplete location object, missing timestamp or accuracy?");
 
         try {
             return mService.injectLocation(location);
@@ -1487,15 +1509,11 @@
         Preconditions.checkArgument(provider != null, "invalid null provider");
         Preconditions.checkArgument(location != null, "invalid null location");
 
-        if (!location.isComplete()) {
-            IllegalArgumentException e = new IllegalArgumentException(
-                    "Incomplete location object, missing timestamp or accuracy? " + location);
-            if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
-                Log.w(TAG, e);
-                location.makeComplete();
-            } else {
-                throw e;
-            }
+        if (Compatibility.isChangeEnabled(INCOMPLETE_LOCATION)) {
+            Preconditions.checkArgument(location.isComplete(),
+                    "incomplete location object, missing timestamp or accuracy?");
+        } else {
+            location.makeComplete();
         }
 
         try {
@@ -1629,7 +1647,11 @@
     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
     public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
             @NonNull PendingIntent intent) {
-        checkPendingIntent(intent);
+        Preconditions.checkArgument(intent != null, "invalid null pending intent");
+        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+            Preconditions.checkArgument(intent.isTargetedToPackage(),
+                    "pending intent must be targeted to a package");
+        }
         if (expiration < 0) expiration = Long.MAX_VALUE;
 
         Geofence fence = Geofence.createCircle(latitude, longitude, radius);
@@ -1659,7 +1681,11 @@
      * permission is not present
      */
     public void removeProximityAlert(@NonNull PendingIntent intent) {
-        checkPendingIntent(intent);
+        Preconditions.checkArgument(intent != null, "invalid null pending intent");
+        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+            Preconditions.checkArgument(intent.isTargetedToPackage(),
+                    "pending intent must be targeted to a package");
+        }
 
         try {
             mService.removeGeofence(null, intent, mContext.getPackageName());
@@ -1709,8 +1735,13 @@
             @NonNull LocationRequest request,
             @NonNull Geofence fence,
             @NonNull PendingIntent intent) {
-        checkPendingIntent(intent);
+        Preconditions.checkArgument(request != null, "invalid null location request");
         Preconditions.checkArgument(fence != null, "invalid null geofence");
+        Preconditions.checkArgument(intent != null, "invalid null pending intent");
+        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+            Preconditions.checkArgument(intent.isTargetedToPackage(),
+                    "pending intent must be targeted to a package");
+        }
 
         try {
             mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
@@ -1737,8 +1768,12 @@
      * @hide
      */
     public void removeGeofence(@NonNull Geofence fence, @NonNull PendingIntent intent) {
-        checkPendingIntent(intent);
         Preconditions.checkArgument(fence != null, "invalid null geofence");
+        Preconditions.checkArgument(intent != null, "invalid null pending intent");
+        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+            Preconditions.checkArgument(intent.isTargetedToPackage(),
+                    "pending intent must be targeted to a package");
+        }
 
         try {
             mService.removeGeofence(fence, intent, mContext.getPackageName());
@@ -1759,7 +1794,11 @@
      * @hide
      */
     public void removeAllGeofences(@NonNull PendingIntent intent) {
-        checkPendingIntent(intent);
+        Preconditions.checkArgument(intent != null, "invalid null pending intent");
+        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+            Preconditions.checkArgument(intent.isTargetedToPackage(),
+                    "pending intent must be targeted to a package");
+        }
 
         try {
             mService.removeGeofence(null, intent, mContext.getPackageName());
@@ -1833,14 +1872,15 @@
      * @param status object containing GPS status details, or null.
      * @return status object containing updated GPS status.
      *
-     * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead.
+     * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. No longer
+     * supported in apps targeting S and above.
      */
     @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
-        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) {
+        if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
             throw new UnsupportedOperationException(
-                    "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
+                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
         }
 
         GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus();
@@ -1863,17 +1903,14 @@
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      *
      * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead. No longer
-     * supported in apps targeting R and above.
+     * supported in apps targeting S and above.
      */
     @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean addGpsStatusListener(GpsStatus.Listener listener) {
-        UnsupportedOperationException ex = new UnsupportedOperationException(
-                "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
-        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) {
-            throw ex;
-        } else {
-            Log.w(TAG, ex);
+        if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+            throw new UnsupportedOperationException(
+                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
         }
 
         try {
@@ -1889,16 +1926,13 @@
      * @param listener GPS status listener object to remove
      *
      * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead. No longer
-     * supported in apps targeting R and above.
+     * supported in apps targeting S and above.
      */
     @Deprecated
     public void removeGpsStatusListener(GpsStatus.Listener listener) {
-        UnsupportedOperationException ex = new UnsupportedOperationException(
-                "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead");
-        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) {
-            throw ex;
-        } else {
-            Log.w(TAG, ex);
+        if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+            throw new UnsupportedOperationException(
+                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
         }
 
         try {
@@ -2162,6 +2196,30 @@
     }
 
     /**
+     * Registers a GNSS Measurement callback.
+     *
+     * @param request  extra parameters to pass to GNSS measurement provider. For example, if {@link
+     *                 GnssRequest#isFullTrackingEnabled()} is true, GNSS chipset switches off duty
+     *                 cycling.
+     * @param executor the executor that the callback runs on.
+     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * @throws IllegalArgumentException if request is null
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException        if the ACCESS_FINE_LOCATION permission is not present
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, LOCATION_HARDWARE})
+    public boolean registerGnssMeasurementsCallback(
+            @NonNull GnssRequest request,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull GnssMeasurementsEvent.Callback callback) {
+        throw new RuntimeException();
+    }
+
+    /**
      * Injects GNSS measurement corrections into the GNSS chipset.
      *
      * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
@@ -2397,19 +2455,6 @@
         }
     }
 
-    private void checkPendingIntent(PendingIntent pendingIntent) {
-        Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
-        if (!pendingIntent.isTargetedToPackage()) {
-            IllegalArgumentException e = new IllegalArgumentException(
-                    "invalid pending intent - must be targeted to package");
-            if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
-                throw e;
-            } else {
-                Log.w(TAG, e);
-            }
-        }
-    }
-
     private static class GetCurrentLocationTransport extends ILocationListener.Stub implements
             AlarmManager.OnAlarmListener {
 
diff --git a/media/java/android/media/AudioDeviceAddress.aidl b/media/java/android/media/AudioDevice.aidl
similarity index 94%
rename from media/java/android/media/AudioDeviceAddress.aidl
rename to media/java/android/media/AudioDevice.aidl
index 6a1a7f7..02071e5 100644
--- a/media/java/android/media/AudioDeviceAddress.aidl
+++ b/media/java/android/media/AudioDevice.aidl
@@ -15,4 +15,4 @@
 
 package android.media;
 
-parcelable AudioDeviceAddress;
+parcelable AudioDevice;
diff --git a/media/java/android/media/AudioDeviceAddress.java b/media/java/android/media/AudioDevice.java
similarity index 84%
rename from media/java/android/media/AudioDeviceAddress.java
rename to media/java/android/media/AudioDevice.java
index 3d8fc37..31ecc7b 100644
--- a/media/java/android/media/AudioDeviceAddress.java
+++ b/media/java/android/media/AudioDevice.java
@@ -28,7 +28,7 @@
 
 /**
  * @hide
- * Class to represent device type (speaker, headset...), address and role (input, output)
+ * Class to represent device type (speaker, headset...), address (if known) and role (input, output)
  * of an audio device.
  * <p>Unlike {@link AudioDeviceInfo}, the device
  * doesn't need to be connected to be uniquely identified, it can
@@ -39,7 +39,7 @@
  * permission, APIs using one rely on MODIFY_AUDIO_ROUTING.
  */
 @SystemApi
-public final class AudioDeviceAddress implements Parcelable {
+public final class AudioDevice implements Parcelable {
 
     /**
      * A role identifying input devices, such as microphones.
@@ -78,7 +78,7 @@
      *                   type and address.
      */
     @SystemApi
-    public AudioDeviceAddress(@NonNull AudioDeviceInfo deviceInfo) {
+    public AudioDevice(@NonNull AudioDeviceInfo deviceInfo) {
         Objects.requireNonNull(deviceInfo);
         mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
         mType = deviceInfo.getType();
@@ -93,7 +93,7 @@
      * @param address the address of the device, or an empty string for devices without one
      */
     @SystemApi
-    public AudioDeviceAddress(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
+    public AudioDevice(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
                               @NonNull String address) {
         Objects.requireNonNull(address);
         if (role != ROLE_OUTPUT && role != ROLE_INPUT) {
@@ -111,7 +111,7 @@
         mAddress = address;
     }
 
-    /*package*/ AudioDeviceAddress(int nativeType, @NonNull String address) {
+    /*package*/ AudioDevice(int nativeType, @NonNull String address) {
         mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
         mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
         mAddress = address;
@@ -157,7 +157,7 @@
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
 
-        AudioDeviceAddress that = (AudioDeviceAddress) o;
+        AudioDevice that = (AudioDevice) o;
         return ((mRole == that.mRole)
                 && (mType == that.mType)
                 && mAddress.equals(that.mAddress));
@@ -170,7 +170,7 @@
 
     @Override
     public String toString() {
-        return new String("AudioDeviceAddress:"
+        return new String("AudioDevice:"
                 + " role:" + roleToString(mRole)
                 + " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(
                         AudioDeviceInfo.convertDeviceTypeToInternalDevice(mType))
@@ -191,25 +191,25 @@
         dest.writeString(mAddress);
     }
 
-    private AudioDeviceAddress(@NonNull Parcel in) {
+    private AudioDevice(@NonNull Parcel in) {
         mRole = in.readInt();
         mType = in.readInt();
         mAddress = in.readString();
     }
 
-    public static final @NonNull Parcelable.Creator<AudioDeviceAddress> CREATOR =
-            new Parcelable.Creator<AudioDeviceAddress>() {
+    public static final @NonNull Parcelable.Creator<AudioDevice> CREATOR =
+            new Parcelable.Creator<AudioDevice>() {
         /**
-         * Rebuilds an AudioDeviceAddress previously stored with writeToParcel().
-         * @param p Parcel object to read the AudioDeviceAddress from
-         * @return a new AudioDeviceAddress created from the data in the parcel
+         * Rebuilds an AudioDevice previously stored with writeToParcel().
+         * @param p Parcel object to read the AudioDevice from
+         * @return a new AudioDevice created from the data in the parcel
          */
-        public AudioDeviceAddress createFromParcel(Parcel p) {
-            return new AudioDeviceAddress(p);
+        public AudioDevice createFromParcel(Parcel p) {
+            return new AudioDevice(p);
         }
 
-        public AudioDeviceAddress[] newArray(int size) {
-            return new AudioDeviceAddress[size];
+        public AudioDevice[] newArray(int size) {
+            return new AudioDevice[size];
         }
     };
 }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7b17f9f..4a1088b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1596,7 +1596,7 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
     public boolean setPreferredDeviceForStrategy(@NonNull AudioProductStrategy strategy,
-            @NonNull AudioDeviceAddress device) {
+            @NonNull AudioDevice device) {
         Objects.requireNonNull(strategy);
         Objects.requireNonNull(device);
         try {
@@ -1611,7 +1611,7 @@
     /**
      * @hide
      * Removes the preferred audio device previously set with
-     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAddress)}.
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDevice)}.
      * @param strategy the audio strategy whose routing will be affected
      * @return true if the operation was successful, false otherwise (invalid strategy, or no
      *     device set for example)
@@ -1632,14 +1632,14 @@
     /**
      * @hide
      * Return the preferred device for an audio strategy, previously set with
-     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceAddress)}
+     * {@link #setPreferredDeviceForStrategy(AudioProductStrategy, AudioDevice)}
      * @param strategy the strategy to query
      * @return the preferred device for that strategy, or null if none was ever set or if the
      *    strategy is invalid
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public @Nullable AudioDeviceAddress getPreferredDeviceForStrategy(
+    public @Nullable AudioDevice getPreferredDeviceForStrategy(
             @NonNull AudioProductStrategy strategy) {
         Objects.requireNonNull(strategy);
         try {
@@ -4379,7 +4379,7 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
-    public @NonNull List<AudioDeviceAddress> getDevicesForAttributes(
+    public @NonNull List<AudioDevice> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
         Objects.requireNonNull(attributes);
         final IAudioService service = getService();
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 02cb8aa..0a0f7f6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1085,18 +1085,18 @@
      * @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(
+    public static @NonNull ArrayList<AudioDevice> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
         Objects.requireNonNull(attributes);
-        final AudioDeviceAddress[] devices = new AudioDeviceAddress[MAX_DEVICE_ROUTING];
+        final AudioDevice[] devices = new AudioDevice[MAX_DEVICE_ROUTING];
         final int res = getDevicesForAttributes(attributes, devices);
-        final ArrayList<AudioDeviceAddress> routeDevices = new ArrayList<>();
+        final ArrayList<AudioDevice> routeDevices = new ArrayList<>();
         if (res != SUCCESS) {
             Log.e(TAG, "error " + res + " in getDevicesForAttributes for " + attributes);
             return routeDevices;
         }
 
-        for (AudioDeviceAddress device : devices) {
+        for (AudioDevice device : devices) {
             if (device != null) {
                 routeDevices.add(device);
             }
@@ -1106,12 +1106,12 @@
 
     /**
      * Maximum number of audio devices a track is ever routed to, determines the size of the
-     * array passed to {@link #getDevicesForAttributes(AudioAttributes, AudioDeviceAddress[])}
+     * array passed to {@link #getDevicesForAttributes(AudioAttributes, AudioDevice[])}
      */
     private static final int MAX_DEVICE_ROUTING = 4;
 
     private static native int getDevicesForAttributes(@NonNull AudioAttributes aa,
-                                                      @NonNull AudioDeviceAddress[] devices);
+                                                      @NonNull AudioDevice[] devices);
 
     /** @hide returns true if master mono is enabled. */
     public static native boolean getMasterMono();
@@ -1246,7 +1246,7 @@
      * @return {@link #SUCCESS} if successfully set
      */
     public static int setPreferredDeviceForStrategy(
-            int strategy, @NonNull AudioDeviceAddress device) {
+            int strategy, @NonNull AudioDevice device) {
         return setPreferredDeviceForStrategy(strategy,
                 AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType()),
                 device.getAddress());
@@ -1277,7 +1277,7 @@
      *     and written to the array
      */
     public static native int getPreferredDeviceForStrategy(int strategy,
-                                                           AudioDeviceAddress[] device);
+                                                           AudioDevice[] device);
 
     // Items shared with audio service
 
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 64c5c05..0fbc0d2 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -18,7 +18,7 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.media.AudioAttributes;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
 import android.media.AudioFocusInfo;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioRecordingConfiguration;
@@ -274,13 +274,13 @@
 
     boolean isCallScreeningModeSupported();
 
-    int setPreferredDeviceForStrategy(in int strategy, in AudioDeviceAddress device);
+    int setPreferredDeviceForStrategy(in int strategy, in AudioDevice device);
 
     int removePreferredDeviceForStrategy(in int strategy);
 
-    AudioDeviceAddress getPreferredDeviceForStrategy(in int strategy);
+    AudioDevice getPreferredDeviceForStrategy(in int strategy);
 
-    List<AudioDeviceAddress> getDevicesForAttributes(in AudioAttributes attributes);
+    List<AudioDevice> getDevicesForAttributes(in AudioAttributes attributes);
 
     int setAllowedCapturePolicy(in int capturePolicy);
 
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index d7b74df..6418610 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -471,7 +471,8 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
-                if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+                if (route.isSystemRoute()
+                        || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                     addedRoutes.add(route);
                 }
             }
@@ -487,7 +488,8 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.remove(route.getId());
-                if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+                if (route.isSystemRoute()
+                        || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                     removedRoutes.add(route);
                 }
             }
@@ -503,7 +505,8 @@
         synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getId(), route);
-                if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+                if (route.isSystemRoute()
+                        || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                     changedRoutes.add(route);
                 }
             }
@@ -643,8 +646,8 @@
     private List<MediaRoute2Info> filterRoutes(List<MediaRoute2Info> routes,
             RouteDiscoveryPreference discoveryRequest) {
         return routes.stream()
-                .filter(
-                        route -> route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
+                .filter(route -> route.isSystemRoute()
+                        || route.hasAnyFeatures(discoveryRequest.getPreferredFeatures()))
                 .collect(Collectors.toList());
     }
 
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index c25a533..6157ef4 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -25,7 +25,7 @@
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
 import android.media.AudioDeviceInfo;
 import android.media.AudioSystem;
 import android.os.Build;
@@ -476,12 +476,12 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
-    public AudioEffect(@NonNull UUID uuid, @NonNull AudioDeviceAddress device) {
+    public AudioEffect(@NonNull UUID uuid, @NonNull AudioDevice device) {
         this(EFFECT_TYPE_NULL, Objects.requireNonNull(uuid), 0, -2, Objects.requireNonNull(device));
     }
 
     private AudioEffect(UUID type, UUID uuid, int priority,
-            int audioSession, @Nullable AudioDeviceAddress device)
+            int audioSession, @Nullable AudioDevice device)
             throws IllegalArgumentException, UnsupportedOperationException,
             RuntimeException {
         int[] id = new int[1];
diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
index 118f65c..0895339 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java
@@ -113,7 +113,7 @@
      * 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}.
+     * this flag is {@link SoundTrigger.ModuleProperties#AUDIO_CAPABILITY_ECHO_CANCELLATION}.
      * If this flag is passed without the audio capability supported, there will be no audio effect
      * applied.
      */
@@ -125,8 +125,9 @@
      * 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.
+     * this flag is {@link SoundTrigger.ModuleProperties#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;
 
@@ -296,10 +297,10 @@
 
         int audioCapabilities = 0;
         if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) {
-            audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION;
+            audioCapabilities |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION;
         }
         if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) {
-            audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION;
+            audioCapabilities |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
         }
 
         int status;
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index dd4dac2..6a8483c 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -173,8 +173,13 @@
         }
 
         /**
-         * Factory constructor to create a SoundModel instance for use with methods in this
-         * class.
+         * Factory constructor to a voice model to be used with {@link SoundTriggerManager}
+         *
+         * @param modelUuid Unique identifier associated with the model.
+         * @param vendorUuid Unique identifier associated the calling vendor.
+         * @param data Model's data.
+         * @param version Version identifier for the model.
+         * @return Voice model
          */
         @NonNull
         public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
@@ -186,8 +191,12 @@
         }
 
         /**
-         * Factory constructor to create a SoundModel instance for use with methods in this
-         * class.
+         * Factory constructor to a voice model to be used with {@link SoundTriggerManager}
+         *
+         * @param modelUuid Unique identifier associated with the model.
+         * @param vendorUuid Unique identifier associated the calling vendor.
+         * @param data Model's data.
+         * @return Voice model
          */
         @NonNull
         public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid,
@@ -195,20 +204,40 @@
             return create(modelUuid, vendorUuid, data, -1);
         }
 
+        /**
+         * Get the model's unique identifier
+         *
+         * @return UUID associated with the model
+         */
         @NonNull
         public UUID getModelUuid() {
             return mGenericSoundModel.uuid;
         }
 
+        /**
+         * Get the model's vendor identifier
+         *
+         * @return UUID associated with the vendor of the model
+         */
         @NonNull
         public UUID getVendorUuid() {
             return mGenericSoundModel.vendorUuid;
         }
 
+        /**
+         * Get the model's version
+         *
+         * @return Version associated with the model
+         */
         public int getVersion() {
             return mGenericSoundModel.version;
         }
 
+        /**
+         * Get the underlying model data
+         *
+         * @return Backing data of the model
+         */
         @Nullable
         public byte[] getModelData() {
             return mGenericSoundModel.data;
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 5e01244..3561f83 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -74,6 +74,8 @@
     private List<Integer> mFrontendIds;
     private Frontend mFrontend;
     private EventHandler mHandler;
+    @Nullable
+    private FrontendInfo mFrontendInfo;
 
     private List<Integer> mLnbIds;
     private Lnb mLnb;
@@ -97,6 +99,7 @@
     public Tuner(@NonNull Context context, @NonNull String tvInputSessionId,
             @TvInputService.PriorityHintUseCaseType int useCase,
             @Nullable OnResourceLostListener listener) {
+        nativeSetup();
         mContext = context;
     }
 
@@ -185,7 +188,7 @@
     /**
      * Listener for resource lost.
      *
-     * <p>Resource is reclaimed and tuner instance is forced to close.
+     * <p>Insufficient resources are reclaimed by higher priority clients.
      */
     public interface OnResourceLostListener {
         /**
@@ -292,6 +295,7 @@
     @Result
     public int tune(@NonNull FrontendSettings settings) {
         TunerUtils.checkTunerPermission(mContext);
+        mFrontendInfo = null;
         return nativeTune(settings.getType(), settings);
     }
 
@@ -333,6 +337,7 @@
         }
         mScanCallback = scanCallback;
         mScanCallbackExecutor = executor;
+        mFrontendInfo = null;
         return nativeScan(settings.getType(), settings, scanType);
     }
 
@@ -468,7 +473,10 @@
         if (mFrontend == null) {
             throw new IllegalStateException("frontend is not initialized");
         }
-        return nativeGetFrontendInfo(mFrontend.mId);
+        if (mFrontendInfo == null) {
+            mFrontendInfo = nativeGetFrontendInfo(mFrontend.mId);
+        }
+        return mFrontendInfo;
     }
 
     /**
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index c1b17d3..63de0334 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -41,8 +41,7 @@
             FRONTEND_STATUS_TYPE_MODULATION, FRONTEND_STATUS_TYPE_SPECTRAL,
             FRONTEND_STATUS_TYPE_LNB_VOLTAGE, FRONTEND_STATUS_TYPE_PLP_ID,
             FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA,
-            FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_VBER_CN,
-            FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, FRONTEND_STATUS_TYPE_MER,
+            FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_MER,
             FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY,
             FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO})
     @Retention(RetentionPolicy.SOURCE)
@@ -125,18 +124,6 @@
     public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
             Constants.FrontendStatusType.LAYER_ERROR;
     /**
-     * CN value by VBER.
-     */
-    public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
-    /**
-     * CN value by LBER.
-     */
-    public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
-    /**
-     * CN value by XER.
-     */
-    public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
-    /**
      * Modulation Error Ratio.
      */
     public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
@@ -223,9 +210,6 @@
     private Integer mAgc;
     private Boolean mIsLnaOn;
     private boolean[] mIsLayerErrors;
-    private Integer mVberCn;
-    private Integer mLberCn;
-    private Integer mXerCn;
     private Integer mMer;
     private Integer mFreqOffset;
     private Integer mHierarchy;
@@ -403,33 +387,6 @@
         return mIsLayerErrors;
     }
     /**
-     * Gets CN value by VBER in thousandths of a deciBel (0.001dB).
-     */
-    public int getVberCn() {
-        if (mVberCn == null) {
-            throw new IllegalStateException();
-        }
-        return mVberCn;
-    }
-    /**
-     * Gets CN value by LBER in thousandths of a deciBel (0.001dB).
-     */
-    public int getLberCn() {
-        if (mLberCn == null) {
-            throw new IllegalStateException();
-        }
-        return mLberCn;
-    }
-    /**
-     * Gets CN value by XER in thousandths of a deciBel (0.001dB).
-     */
-    public int getXerCn() {
-        if (mXerCn == null) {
-            throw new IllegalStateException();
-        }
-        return mXerCn;
-    }
-    /**
      * Gets Modulation Error Ratio in thousandths of a deciBel (0.001dB).
      */
     public int getMer() {
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index f90144b..8105c74 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -67,7 +67,7 @@
     /** Frontend hierarchy. */
     void onHierarchy(@DvbtFrontendSettings.Hierarchy int hierarchy);
 
-    /** Frontend hierarchy. */
+    /** Frontend signal type. */
     void onSignalType(@AnalogFrontendSettings.SignalType int signalType);
 
 }
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 9b37f95..71ba59c 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -724,18 +724,21 @@
     }
 
     if (buffer->size() > 0) {
-        // asC2Buffer clears internal reference, so set the reference again.
         std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer();
-        buffer->copy(c2Buffer);
         if (c2Buffer) {
+            // asC2Buffer clears internal reference, so set the reference again.
+            buffer->copy(c2Buffer);
             switch (c2Buffer->data().type()) {
                 case C2BufferData::LINEAR: {
                     std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
                     context->mBuffer = c2Buffer;
                     ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
                             gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
-                    env->SetLongField(
-                            linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+                    env->CallVoidMethod(
+                            linearBlock.get(),
+                            gLinearBlockInfo.setInternalStateId,
+                            (jlong)context.release(),
+                            true);
                     env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
                     break;
                 }
@@ -744,8 +747,11 @@
                     context->mBuffer = c2Buffer;
                     ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
                             gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
-                    env->SetLongField(
-                            graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+                    env->CallVoidMethod(
+                            graphicBlock.get(),
+                            gGraphicBlockInfo.setInternalStateId,
+                            (jlong)context.release(),
+                            true);
                     env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
                     break;
                 }
@@ -761,16 +767,22 @@
                 context->mLegacyBuffer = buffer;
                 ScopedLocalRef<jobject> linearBlock{env, env->NewObject(
                         gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)};
-                env->SetLongField(
-                        linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release());
+                env->CallVoidMethod(
+                        linearBlock.get(),
+                        gLinearBlockInfo.setInternalStateId,
+                        (jlong)context.release(),
+                        true);
                 env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get());
             } else {
                 std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock};
                 context->mLegacyBuffer = buffer;
                 ScopedLocalRef<jobject> graphicBlock{env, env->NewObject(
                         gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)};
-                env->SetLongField(
-                        graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release());
+                env->CallVoidMethod(
+                        graphicBlock.get(),
+                        gGraphicBlockInfo.setInternalStateId,
+                        (jlong)context.release(),
+                        true);
                 env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get());
             }
         }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 81a85c9..08c3f98 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -322,6 +322,179 @@
             (jint) jId);
 }
 
+jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
+
+    jint typeCap = caps.analogCaps().typeCap;
+    jint sifStandardCap = caps.analogCaps().sifStandardCap;
+    return env->NewObject(clazz, capsInit, typeCap, sifStandardCap);
+}
+
+jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V");
+
+    jint bandwidthCap = caps.atsc3Caps().bandwidthCap;
+    jint modulationCap = caps.atsc3Caps().modulationCap;
+    jint timeInterleaveModeCap = caps.atsc3Caps().timeInterleaveModeCap;
+    jint codeRateCap = caps.atsc3Caps().codeRateCap;
+    jint fecCap = caps.atsc3Caps().fecCap;
+    jint demodOutputFormatCap = caps.atsc3Caps().demodOutputFormatCap;
+
+    return env->NewObject(clazz, capsInit, bandwidthCap, modulationCap, timeInterleaveModeCap,
+            codeRateCap, fecCap, demodOutputFormatCap);
+}
+
+jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(I)V");
+
+    jint modulationCap = caps.atscCaps().modulationCap;
+
+    return env->NewObject(clazz, capsInit, modulationCap);
+}
+
+jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(III)V");
+
+    jint modulationCap = caps.dvbcCaps().modulationCap;
+    jint fecCap = caps.dvbcCaps().fecCap;
+    jint annexCap = caps.dvbcCaps().annexCap;
+
+    return env->NewObject(clazz, capsInit, modulationCap, fecCap, annexCap);
+}
+
+jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IJI)V");
+
+    jint modulationCap = caps.dvbsCaps().modulationCap;
+    jlong innerfecCap = caps.dvbsCaps().innerfecCap;
+    jint standard = caps.dvbsCaps().standard;
+
+    return env->NewObject(clazz, capsInit, modulationCap, innerfecCap, standard);
+}
+
+jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIZZ)V");
+
+    jint transmissionModeCap = caps.dvbtCaps().transmissionModeCap;
+    jint bandwidthCap = caps.dvbtCaps().bandwidthCap;
+    jint constellationCap = caps.dvbtCaps().constellationCap;
+    jint coderateCap = caps.dvbtCaps().coderateCap;
+    jint hierarchyCap = caps.dvbtCaps().hierarchyCap;
+    jint guardIntervalCap = caps.dvbtCaps().guardIntervalCap;
+    jboolean isT2Supported = caps.dvbtCaps().isT2Supported;
+    jboolean isMisoSupported = caps.dvbtCaps().isMisoSupported;
+
+    return env->NewObject(clazz, capsInit, transmissionModeCap, bandwidthCap, constellationCap,
+            coderateCap, hierarchyCap, guardIntervalCap, isT2Supported, isMisoSupported);
+}
+
+jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
+
+    jint modulationCap = caps.isdbs3Caps().modulationCap;
+    jint coderateCap = caps.isdbs3Caps().coderateCap;
+
+    return env->NewObject(clazz, capsInit, modulationCap, coderateCap);
+}
+
+jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
+
+    jint modulationCap = caps.isdbsCaps().modulationCap;
+    jint coderateCap = caps.isdbsCaps().coderateCap;
+
+    return env->NewObject(clazz, capsInit, modulationCap, coderateCap);
+}
+
+jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) {
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities");
+    jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIII)V");
+
+    jint modeCap = caps.isdbtCaps().modeCap;
+    jint bandwidthCap = caps.isdbtCaps().bandwidthCap;
+    jint modulationCap = caps.isdbtCaps().modulationCap;
+    jint coderateCap = caps.isdbtCaps().coderateCap;
+    jint guardIntervalCap = caps.isdbtCaps().guardIntervalCap;
+
+    return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap,
+            guardIntervalCap);
+}
+
+jobject JTuner::getFrontendInfo(int id) {
+    FrontendInfo feInfo;
+    Result res;
+    mTuner->getFrontendInfo(id, [&](Result r, const FrontendInfo& info) {
+        feInfo = info;
+        res = r;
+    });
+    if (res != Result::SUCCESS) {
+        return NULL;
+    }
+
+    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo");
+    jmethodID infoInit = env->GetMethodID(clazz, "<init>",
+            "(IIIIIIII[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
+
+    jint type = (jint) feInfo.type;
+    jint minFrequency = feInfo.minFrequency;
+    jint maxFrequency = feInfo.maxFrequency;
+    jint minSymbolRate = feInfo.minSymbolRate;
+    jint maxSymbolRate = feInfo.maxSymbolRate;
+    jint acquireRange = feInfo.acquireRange;
+    jint exclusiveGroupId = feInfo.exclusiveGroupId;
+    jintArray statusCaps = env->NewIntArray(feInfo.statusCaps.size());
+    env->SetIntArrayRegion(
+            statusCaps, 0, feInfo.statusCaps.size(),
+            reinterpret_cast<jint*>(&feInfo.statusCaps[0]));
+    FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps;
+
+    jobject jcaps = NULL;
+    switch(feInfo.type) {
+        case FrontendType::ANALOG:
+            jcaps = getAnalogFrontendCaps(env, caps);
+            break;
+        case FrontendType::ATSC3:
+            jcaps = getAtsc3FrontendCaps(env, caps);
+            break;
+        case FrontendType::ATSC:
+            jcaps = getAtscFrontendCaps(env, caps);
+            break;
+        case FrontendType::DVBC:
+            jcaps = getDvbcFrontendCaps(env, caps);
+            break;
+        case FrontendType::DVBS:
+            jcaps = getDvbsFrontendCaps(env, caps);
+            break;
+        case FrontendType::DVBT:
+            jcaps = getDvbtFrontendCaps(env, caps);
+            break;
+        case FrontendType::ISDBS:
+            jcaps = getIsdbsFrontendCaps(env, caps);
+            break;
+        case FrontendType::ISDBS3:
+            jcaps = getIsdbs3FrontendCaps(env, caps);
+            break;
+        case FrontendType::ISDBT:
+            jcaps = getIsdbtFrontendCaps(env, caps);
+            break;
+        default:
+            break;
+    }
+
+    return env->NewObject(
+            clazz, infoInit, (jint) id, type, minFrequency, maxFrequency, minSymbolRate,
+            maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps);
+}
+
 jobject JTuner::getLnbIds() {
     ALOGD("JTuner::getLnbIds()");
     mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) {
@@ -1162,8 +1335,9 @@
     return 0;
 }
 
-static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv*, jobject, jint) {
-    return NULL;
+static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->getFrontendInfo(id);
 }
 
 static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 978d9c6..cfe99b3 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -39,6 +39,7 @@
 using ::android::hardware::tv::tuner::V1_0::DvrType;
 using ::android::hardware::tv::tuner::V1_0::FrontendEventType;
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
+using ::android::hardware::tv::tuner::V1_0::FrontendInfo;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
@@ -132,6 +133,7 @@
     sp<ITuner> getTunerService();
     jobject getFrontendIds();
     jobject openFrontendById(int id);
+    jobject getFrontendInfo(int id);
     int tune(const FrontendSettings& settings);
     int stopTune();
     int scan(const FrontendSettings& settings, FrontendScanType scanType);
@@ -158,6 +160,15 @@
     sp<ILnb> mLnb;
     sp<IDemux> mDemux;
     int mDemuxId;
+    static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
+    static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps);
 };
 
 }  // namespace android
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index c1143ce..2e4d214 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -213,12 +213,12 @@
         return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
     }
 
-    // Note: This recomputes the data space because it's possible the client has
-    // changed the output color space, so we cannot rely on it. Alternatively,
+    // Note: This recomputes the color type because it's possible the client has
+    // changed the output color type, so we cannot rely on it. Alternatively,
     // we could store the ADataSpace in the ImageDecoder.
     const ImageDecoder* imageDecoder = toDecoder(info);
     SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType);
-    sk_sp<SkColorSpace> colorSpace = imageDecoder->mCodec->computeOutputColorSpace(colorType);
+    sk_sp<SkColorSpace> colorSpace = imageDecoder->getDefaultColorSpace();
     return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType);
 }
 
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 38cf5ab..617305c 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -38,7 +38,6 @@
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Path;
 import android.provider.DocumentsContract.Root;
-import android.provider.MediaStore;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -100,7 +99,6 @@
 
     private static final String ROOT_ID_PRIMARY_EMULATED =
             DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID;
-    private static final String ROOT_ID_HOME = "home";
 
     private static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
     private static final String GET_MEDIA_URI_CALL = "get_media_uri";
@@ -156,7 +154,6 @@
     private void updateVolumesLocked() {
         mRoots.clear();
 
-        VolumeInfo primaryVolume = null;
         final int userId = UserHandle.myUserId();
         final List<VolumeInfo> volumes = mStorageManager.getVolumes();
         for (VolumeInfo volume : volumes) {
@@ -234,8 +231,6 @@
             }
 
             if (volume.isPrimary()) {
-                // save off the primary volume for subsequent "Home" dir initialization.
-                primaryVolume = volume;
                 root.flags |= Root.FLAG_ADVANCED;
             }
             // Dunno when this would NOT be the case, but never hurts to be correct.
@@ -259,37 +254,6 @@
             }
         }
 
-        // Finally, if primary storage is available we add the "Documents" directory.
-        // If I recall correctly the actual directory is created on demand
-        // by calling either getPathForUser, or getInternalPathForUser.
-        if (primaryVolume != null && primaryVolume.isVisible()) {
-            final RootInfo root = new RootInfo();
-            root.rootId = ROOT_ID_HOME;
-            mRoots.put(root.rootId, root);
-            root.title = getContext().getString(R.string.root_documents);
-
-            // Only report bytes on *volumes*...as a matter of policy.
-            root.reportAvailableBytes = false;
-            root.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_SEARCH
-                    | Root.FLAG_SUPPORTS_IS_CHILD;
-
-            // Dunno when this would NOT be the case, but never hurts to be correct.
-            if (primaryVolume.isMountedWritable()) {
-                root.flags |= Root.FLAG_SUPPORTS_CREATE;
-            }
-
-            // Create the "Documents" directory on disk (don't use the localized title).
-            root.visiblePath = new File(
-                    primaryVolume.getPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
-            root.path = new File(
-                    primaryVolume.getInternalPathForUser(userId), Environment.DIRECTORY_DOCUMENTS);
-            try {
-                root.docId = getDocIdForFile(root.path);
-            } catch (FileNotFoundException e) {
-                throw new IllegalStateException(e);
-            }
-        }
-
         Log.d(TAG, "After updating volumes, found " + mRoots.size() + " active roots");
 
         // Note this affects content://com.android.externalstorage.documents/root/39BD-07C5
diff --git a/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml b/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
index a06dc54..c4d8f35 100644
--- a/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
+++ b/packages/Incremental/NativeAdbDataLoader/AndroidManifest.xml
@@ -29,7 +29,7 @@
         <service android:enabled="true"
                  android:name="com.android.incremental.nativeadb.NativeAdbDataLoaderService"
                  android:label="@string/app_name"
-                 android:exported="true">
+                 android:exported="false">
             <intent-filter>
                 <action android:name="android.intent.action.LOAD_DATA" />
            </intent-filter>
diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml
new file mode 100644
index 0000000..04de174
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_blue.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
+      android:fillColor="#4285F4"/>
+</vector>
diff --git a/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml
new file mode 100644
index 0000000..b4145f2
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/drawable/ic_swap_horiz_grey.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <path
+      android:pathData="M6.99,11L3,15l3.99,4v-3H14v-2H6.99v-3zM21,9l-3.99,-4v3H10v2h7.01v3L21,9z"
+      android:fillColor="#757575"/>
+</vector>
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
new file mode 100644
index 0000000..e6f8c01
--- /dev/null
+++ b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
@@ -0,0 +1,92 @@
+<?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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/entity_header"
+    style="@style/EntityHeader"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="horizontal">
+
+    <LinearLayout
+        android:id="@+id/entity_header_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:gravity="center_horizontal"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/entity_header_content"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+
+            <ImageView
+                android:id="@+id/entity_header_icon_personal"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:scaleType="fitCenter"
+                android:antialias="true"/>
+
+            <TextView
+                android:id="@+id/install_type"
+                style="@style/TextAppearance.EntityHeaderSummary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="2dp"
+                android:text="Personal"/>
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/entity_header_swap_horiz"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:scaleType="fitCenter"
+            android:antialias="true"
+            android:src="@drawable/ic_swap_horiz_grey"/>
+
+        <LinearLayout
+            android:id="@+id/entity_header_content"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+
+            <ImageView
+                android:id="@+id/entity_header_icon_work"
+                android:layout_width="48dp"
+                android:layout_height="48dp"
+                android:scaleType="fitCenter"
+                android:antialias="true"/>
+            <TextView
+                android:id="@+id/install_type"
+                style="@style/TextAppearance.EntityHeaderSummary"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="2dp"
+                android:text="Work"/>
+        </LinearLayout>
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
new file mode 100644
index 0000000..332d6c7
--- /dev/null
+++ b/packages/SettingsLib/res/values/config.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+<!-- These resources are around just to allow their values to be customized -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V -->
+    <integer name="config_chargingSlowlyThreshold">5000000</integer>
+
+    <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V  -->
+    <integer name="config_chargingFastThreshold">7500000</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index dd21e5c..4d96251 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -970,7 +970,7 @@
     <!-- Title for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
     <string name="accessibility_display_daltonizer_preference_title">Color correction</string>
     <!-- Subtitle for the accessibility preference to configure display color space correction. [CHAR LIMIT=NONE] -->
-    <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with color blindness to see more accurate colors</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle">Color correction helps people with colorblindness see more accurate colors</string>
     <!-- Summary shown for color space correction preference when its value is overridden by another preference [CHAR LIMIT=35] -->
     <string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
 
@@ -1034,8 +1034,10 @@
     <string name="battery_info_status_unknown">Unknown</string>
     <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging from an unknown source.  -->
     <string name="battery_info_status_charging">Charging</string>
-    <!-- [CHAR_LIMIT=20] Battery use screen with lower case.  Battery status shown in chart label when charging from an unknown source.  -->
-    <string name="battery_info_status_charging_lower">charging</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging speed is fast.  -->
+    <string name="battery_info_status_charging_fast">Charging rapidly</string>
+    <!-- [CHAR_LIMIT=20] Battery use screen.  Battery status shown in chart label when charging speed is slow.  -->
+    <string name="battery_info_status_charging_slow">Charging slowly</string>
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_discharging">Not charging</string>
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 213e365..f485793 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -35,6 +35,7 @@
 import com.android.internal.util.UserIcons;
 import com.android.launcher3.icons.IconFactory;
 import com.android.settingslib.drawable.UserIconDrawable;
+import com.android.settingslib.fuelgauge.BatteryStatus;
 
 import java.text.NumberFormat;
 
@@ -122,7 +123,7 @@
     public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
         final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
-            Drawable drawable =  UserIconDrawable.getManagedUserDrawable(context);
+            Drawable drawable = UserIconDrawable.getManagedUserDrawable(context);
             drawable.setBounds(0, 0, iconSize, iconSize);
             return drawable;
         }
@@ -164,20 +165,43 @@
         return (level * 100) / scale;
     }
 
-    public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) {
-        int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
+    /**
+     * Get battery status string
+     *
+     * @param context the context
+     * @param batteryChangedIntent battery broadcast intent received from {@link
+     *                             Intent.ACTION_BATTERY_CHANGED}.
+     * @return battery status string
+     */
+    public static String getBatteryStatus(Context context, Intent batteryChangedIntent) {
+        final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
                 BatteryManager.BATTERY_STATUS_UNKNOWN);
-        String statusString;
-        if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
-            statusString = res.getString(R.string.battery_info_status_charging);
-        } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
-            statusString = res.getString(R.string.battery_info_status_discharging);
-        } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
-            statusString = res.getString(R.string.battery_info_status_not_charging);
-        } else if (status == BatteryManager.BATTERY_STATUS_FULL) {
+        final Resources res = context.getResources();
+
+        String statusString = res.getString(R.string.battery_info_status_unknown);
+        final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent);
+
+        if (batteryStatus.isCharged()) {
             statusString = res.getString(R.string.battery_info_status_full);
         } else {
-            statusString = res.getString(R.string.battery_info_status_unknown);
+            if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
+                switch (batteryStatus.getChargingSpeed(context)) {
+                    case BatteryStatus.CHARGING_FAST:
+                        statusString = res.getString(R.string.battery_info_status_charging_fast);
+                        break;
+                    case BatteryStatus.CHARGING_SLOWLY:
+                        statusString = res.getString(R.string.battery_info_status_charging_slow);
+                        break;
+                    default:
+                        statusString = res.getString(R.string.battery_info_status_charging);
+                        break;
+                }
+
+            } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+                statusString = res.getString(R.string.battery_info_status_discharging);
+            } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
+                statusString = res.getString(R.string.battery_info_status_not_charging);
+            }
         }
 
         return statusString;
@@ -211,7 +235,7 @@
     /**
      * This method computes disabled color from normal color
      *
-     * @param context
+     * @param context the context
      * @param inputColor normal color.
      * @return disabled color.
      */
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
new file mode 100644
index 0000000..bc40903
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java
@@ -0,0 +1,147 @@
+/*
+ * 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.settingslib.fuelgauge;
+
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_HEALTH;
+import static android.os.BatteryManager.EXTRA_LEVEL;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
+import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_STATUS;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+
+import com.android.settingslib.R;
+
+/**
+ * Stores and computes some battery information.
+ */
+public class BatteryStatus {
+    private static final int LOW_BATTERY_THRESHOLD = 20;
+    private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
+
+    public static final int CHARGING_UNKNOWN = -1;
+    public static final int CHARGING_SLOWLY = 0;
+    public static final int CHARGING_REGULAR = 1;
+    public static final int CHARGING_FAST = 2;
+
+    public final int status;
+    public final int level;
+    public final int plugged;
+    public final int health;
+    public final int maxChargingWattage;
+
+    public BatteryStatus(int status, int level, int plugged, int health,
+            int maxChargingWattage) {
+        this.status = status;
+        this.level = level;
+        this.plugged = plugged;
+        this.health = health;
+        this.maxChargingWattage = maxChargingWattage;
+    }
+
+    public BatteryStatus(Intent batteryChangedIntent) {
+        status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
+        plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0);
+        level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0);
+        health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+
+        final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT,
+                -1);
+        int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
+
+        if (maxChargingMicroVolt <= 0) {
+            maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
+        }
+        if (maxChargingMicroAmp > 0) {
+            // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor
+            // to maintain precision equally on both factors.
+            maxChargingWattage = (maxChargingMicroAmp / 1000)
+                    * (maxChargingMicroVolt / 1000);
+        } else {
+            maxChargingWattage = -1;
+        }
+    }
+
+    /**
+     * Determine whether the device is plugged in (USB, power, or wireless).
+     *
+     * @return true if the device is plugged in.
+     */
+    public boolean isPluggedIn() {
+        return plugged == BatteryManager.BATTERY_PLUGGED_AC
+                || plugged == BatteryManager.BATTERY_PLUGGED_USB
+                || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
+    }
+
+    /**
+     * Determine whether the device is plugged in (USB, power).
+     *
+     * @return true if the device is plugged in wired (as opposed to wireless)
+     */
+    public boolean isPluggedInWired() {
+        return plugged == BatteryManager.BATTERY_PLUGGED_AC
+                || plugged == BatteryManager.BATTERY_PLUGGED_USB;
+    }
+
+    /**
+     * Whether or not the device is charged. Note that some devices never return 100% for
+     * battery level, so this allows either battery level or status to determine if the
+     * battery is charged.
+     *
+     * @return true if the device is charged
+     */
+    public boolean isCharged() {
+        return status == BATTERY_STATUS_FULL || level >= 100;
+    }
+
+    /**
+     * Whether battery is low and needs to be charged.
+     *
+     * @return true if battery is low
+     */
+    public boolean isBatteryLow() {
+        return level < LOW_BATTERY_THRESHOLD;
+    }
+
+    /**
+     * Return current chargin speed is fast, slow or normal.
+     *
+     * @return the charing speed
+     */
+    public final int getChargingSpeed(Context context) {
+        final int slowThreshold = context.getResources().getInteger(
+                R.integer.config_chargingSlowlyThreshold);
+        final int fastThreshold = context.getResources().getInteger(
+                R.integer.config_chargingFastThreshold);
+        return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
+                maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
+                        maxChargingWattage > fastThreshold ? CHARGING_FAST :
+                                CHARGING_REGULAR;
+    }
+
+    @Override
+    public String toString() {
+        return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
+                + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3a53d29..e551b69 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -19,7 +19,8 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 import android.util.Pair;
 
 import com.android.settingslib.R;
@@ -35,8 +36,9 @@
 
     private CachedBluetoothDevice mCachedDevice;
 
-    BluetoothMediaDevice(Context context, CachedBluetoothDevice device) {
-        super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+    BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
+            MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
+        super(context, MediaDeviceType.TYPE_BLUETOOTH_DEVICE, routerManager, info, packageName);
         mCachedDevice = device;
         initDeviceRecord();
     }
@@ -65,20 +67,6 @@
         return MediaDeviceUtils.getId(mCachedDevice);
     }
 
-    @Override
-    public boolean connect() {
-        //TODO(b/117129183): add callback to notify LocalMediaManager connection state.
-        final boolean isConnected = mCachedDevice.setActive();
-        setConnectedRecord();
-        Log.d(TAG, "connect() device : " + getName() + ", is selected : " + isConnected);
-        return isConnected;
-    }
-
-    @Override
-    public void disconnect() {
-        //TODO(b/117129183): disconnected last select device
-    }
-
     /**
      * Get current CachedBluetoothDevice
      */
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
index 3a807c9..d84788b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java
@@ -157,7 +157,7 @@
     private void addMediaDevice(CachedBluetoothDevice cachedDevice) {
         MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(cachedDevice));
         if (mediaDevice == null) {
-            mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice);
+            mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, null, null, null);
             cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
             mLastAddedDevice = mediaDevice;
             mMediaDevices.add(mediaDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index d287f95..b9081f2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,9 +16,12 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
+import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -30,16 +33,9 @@
 
     private static final String TAG = "InfoMediaDevice";
 
-    private final MediaRoute2Info mRouteInfo;
-    private final MediaRouter2Manager mRouterManager;
-    private final String mPackageName;
-
     InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
             String packageName) {
-        super(context, MediaDeviceType.TYPE_CAST_DEVICE);
-        mRouterManager = routerManager;
-        mRouteInfo = info;
-        mPackageName = packageName;
+        super(context, MediaDeviceType.TYPE_CAST_DEVICE, routerManager, info, packageName);
         initDeviceRecord();
     }
 
@@ -67,13 +63,6 @@
     }
 
     @Override
-    public boolean connect() {
-        setConnectedRecord();
-        mRouterManager.selectRoute(mPackageName, mRouteInfo);
-        return true;
-    }
-
-    @Override
     public void requestSetVolume(int volume) {
         mRouterManager.requestSetVolume(mRouteInfo, volume);
     }
@@ -89,11 +78,30 @@
     }
 
     @Override
-    public void disconnect() {
-        //TODO(b/144535188): disconnected last select device
+    public String getClientPackageName() {
+        return mRouteInfo.getClientPackageName();
     }
 
     @Override
+    public String getClientAppLabel() {
+        final String packageName = mRouteInfo.getClientPackageName();
+        if (TextUtils.isEmpty(packageName)) {
+            Log.d(TAG, "Client package name is empty");
+            return mContext.getResources().getString(R.string.unknown);
+        }
+        try {
+            final PackageManager packageManager = mContext.getPackageManager();
+            final String appLabel = packageManager.getApplicationLabel(
+                    packageManager.getApplicationInfo(packageName, 0)).toString();
+            if (!TextUtils.isEmpty(appLabel)) {
+                return appLabel;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "unable to find " + packageName);
+        }
+        return mContext.getResources().getString(R.string.unknown);
+    }
+
     public boolean isConnected() {
         return true;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index a4be46c..e910967 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -15,13 +15,23 @@
  */
 package com.android.settingslib.media;
 
+import static android.media.MediaRoute2Info.DEVICE_TYPE_BLUETOOTH;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.DEVICE_TYPE_UNKNOWN;
+
 import android.app.Notification;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -44,11 +54,14 @@
     String mPackageName;
 
     private MediaDevice mCurrentConnectedDevice;
+    private LocalBluetoothManager mBluetoothManager;
 
-    public InfoMediaManager(Context context, String packageName, Notification notification) {
+    public InfoMediaManager(Context context, String packageName, Notification notification,
+            LocalBluetoothManager localBluetoothManager) {
         super(context, notification);
 
         mRouterManager = MediaRouter2Manager.getInstance(context);
+        mBluetoothManager = localBluetoothManager;
         if (!TextUtils.isEmpty(packageName)) {
             mPackageName = packageName;
         }
@@ -94,23 +107,73 @@
 
     private void buildAllRoutes() {
         for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
-            final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
-                    mPackageName);
-            mMediaDevices.add(device);
+            addMediaDevice(route);
         }
     }
 
     private void buildAvailableRoutes() {
         for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
-            final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
-                    mPackageName);
-            if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
-                mCurrentConnectedDevice = device;
-            }
-            mMediaDevices.add(device);
+            addMediaDevice(route);
         }
     }
 
+    private void addMediaDevice(MediaRoute2Info route) {
+        final int deviceType = route.getDeviceType();
+        MediaDevice mediaDevice = null;
+        switch (deviceType) {
+            case DEVICE_TYPE_UNKNOWN:
+                //TODO(b/148765806): use correct device type once api is ready.
+                final String defaultRoute = "DEFAULT_ROUTE";
+                if (TextUtils.equals(defaultRoute, route.getOriginalId())) {
+                    mediaDevice =
+                            new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
+                } else {
+                    mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
+                            mPackageName);
+                    if (!TextUtils.isEmpty(mPackageName)
+                            && TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+                        mCurrentConnectedDevice = mediaDevice;
+                    }
+                }
+                break;
+            case DEVICE_TYPE_REMOTE_TV:
+                break;
+            case DEVICE_TYPE_BLUETOOTH:
+                final BluetoothDevice device =
+                        BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getOriginalId());
+                final CachedBluetoothDevice cachedDevice =
+                        mBluetoothManager.getCachedDeviceManager().findDevice(device);
+                mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, mRouterManager,
+                        route, mPackageName);
+                break;
+            default:
+                Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
+                break;
+
+        }
+
+        if (mediaDevice != null) {
+            mMediaDevices.add(mediaDevice);
+        }
+    }
+
+    /**
+     * Transfer MediaDevice for media without package name.
+     */
+    public boolean connectDeviceWithoutPackageName(MediaDevice device) {
+        boolean isConnected = false;
+        final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
+        if (infos.size() > 0) {
+            final RoutingSessionInfo info = infos.get(0);
+            final MediaRouter2Manager.RoutingController controller =
+                    mRouterManager.getControllerForSession(info);
+
+            controller.transferToRoute(device.mRouteInfo);
+            isConnected = true;
+        }
+        return isConnected;
+    }
+
     class RouterManagerCallback extends MediaRouter2Manager.Callback {
 
         @Override
@@ -124,5 +187,10 @@
                 refreshDevices();
             }
         }
+
+        @Override
+        public void onRoutesChanged(List<MediaRoute2Info> routes) {
+            refreshDevices();
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 50196d2..a1342ec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -18,6 +18,7 @@
 import android.app.Notification;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.IntDef;
@@ -57,7 +58,6 @@
     final MediaDeviceCallback mMediaDeviceCallback = new MediaDeviceCallback();
 
     private Context mContext;
-    private BluetoothMediaManager mBluetoothMediaManager;
     private LocalBluetoothManager mLocalBluetoothManager;
     private InfoMediaManager mInfoMediaManager;
     private String mPackageName;
@@ -97,18 +97,18 @@
             return;
         }
 
-        mBluetoothMediaManager =
-                new BluetoothMediaManager(context, mLocalBluetoothManager, notification);
-        mInfoMediaManager = new InfoMediaManager(context, packageName, notification);
+        mInfoMediaManager =
+                new InfoMediaManager(context, packageName, notification, mLocalBluetoothManager);
     }
 
     @VisibleForTesting
     LocalMediaManager(Context context, LocalBluetoothManager localBluetoothManager,
-            BluetoothMediaManager bluetoothMediaManager, InfoMediaManager infoMediaManager) {
+            InfoMediaManager infoMediaManager, String packageName) {
         mContext = context;
         mLocalBluetoothManager = localBluetoothManager;
-        mBluetoothMediaManager = bluetoothMediaManager;
         mInfoMediaManager = infoMediaManager;
+        mPackageName = packageName;
+
     }
 
     /**
@@ -135,7 +135,12 @@
             mCurrentConnectedDevice.disconnect();
         }
 
-        final boolean isConnected = device.connect();
+        boolean isConnected = false;
+        if (TextUtils.isEmpty(mPackageName)) {
+            isConnected = mInfoMediaManager.connectDeviceWithoutPackageName(device);
+        } else {
+            isConnected = device.connect();
+        }
         if (isConnected) {
             mCurrentConnectedDevice = device;
         }
@@ -159,29 +164,10 @@
      */
     public void startScan() {
         mMediaDevices.clear();
-        mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
-        mBluetoothMediaManager.startScan();
         mInfoMediaManager.registerCallback(mMediaDeviceCallback);
         mInfoMediaManager.startScan();
     }
 
-    private void addPhoneDeviceIfNecessary() {
-        // add phone device to list if there have any Bluetooth device and cast device.
-        if (mMediaDevices.size() > 0 && !mMediaDevices.contains(mPhoneDevice)) {
-            if (mPhoneDevice == null) {
-                mPhoneDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
-            }
-            mMediaDevices.add(mPhoneDevice);
-        }
-    }
-
-    private void removePhoneMediaDeviceIfNecessary() {
-        // if PhoneMediaDevice is the last item in the list, remove it.
-        if (mMediaDevices.size() == 1 && mMediaDevices.contains(mPhoneDevice)) {
-            mMediaDevices.clear();
-        }
-    }
-
     void dispatchDeviceListUpdate() {
         synchronized (mCallbacks) {
             Collections.sort(mMediaDevices, COMPARATOR);
@@ -203,8 +189,6 @@
      * Stop scan MediaDevice
      */
     public void stopScan() {
-        mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
-        mBluetoothMediaManager.stopScan();
         mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
         mInfoMediaManager.stopScan();
     }
@@ -227,6 +211,22 @@
     }
 
     /**
+     * Find the MediaDevice from all media devices by id.
+     *
+     * @param id the unique id of MediaDevice
+     * @return MediaDevice
+     */
+    public MediaDevice getMediaDeviceById(String id) {
+        for (MediaDevice mediaDevice : mMediaDevices) {
+            if (mediaDevice.getId().equals(id)) {
+                return mediaDevice;
+            }
+        }
+        Log.i(TAG, "Unable to find device " + id);
+        return null;
+    }
+
+    /**
      * Find the current connected MediaDevice.
      *
      * @return MediaDevice
@@ -235,15 +235,34 @@
         return mCurrentConnectedDevice;
     }
 
+    /**
+     * Find the active MediaDevice.
+     *
+     * @param type the media device type.
+     * @return MediaDevice list
+     */
+    public List<MediaDevice> getActiveMediaDevice(@MediaDevice.MediaDeviceType int type) {
+        final List<MediaDevice> devices = new ArrayList<>();
+        for (MediaDevice device : mMediaDevices) {
+            if (type == device.mType && device.getClientPackageName() != null) {
+                devices.add(device);
+            }
+        }
+        return devices;
+    }
+
     private MediaDevice updateCurrentConnectedDevice() {
+        MediaDevice phoneMediaDevice = null;
         for (MediaDevice device : mMediaDevices) {
             if (device instanceof  BluetoothMediaDevice) {
                 if (isConnected(((BluetoothMediaDevice) device).getCachedDevice())) {
                     return device;
                 }
+            } else if (device instanceof PhoneMediaDevice) {
+                phoneMediaDevice = device;
             }
         }
-        return mMediaDevices.contains(mPhoneDevice) ? mPhoneDevice : null;
+        return mMediaDevices.contains(phoneMediaDevice) ? phoneMediaDevice : null;
     }
 
     private boolean isConnected(CachedBluetoothDevice device) {
@@ -256,38 +275,24 @@
         public void onDeviceAdded(MediaDevice device) {
             if (!mMediaDevices.contains(device)) {
                 mMediaDevices.add(device);
-                addPhoneDeviceIfNecessary();
                 dispatchDeviceListUpdate();
             }
         }
 
         @Override
         public void onDeviceListAdded(List<MediaDevice> devices) {
-            for (MediaDevice device : devices) {
-                if (getMediaDeviceById(mMediaDevices, device.getId()) == null) {
-                    mMediaDevices.add(device);
-                }
-            }
-            addPhoneDeviceIfNecessary();
+            mMediaDevices.clear();
+            mMediaDevices.addAll(devices);
             final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
             mCurrentConnectedDevice = infoMediaDevice != null
                     ? infoMediaDevice : updateCurrentConnectedDevice();
-            updatePhoneMediaDeviceSummary();
             dispatchDeviceListUpdate();
         }
 
-        private void updatePhoneMediaDeviceSummary() {
-            if (mPhoneDevice != null) {
-                ((PhoneMediaDevice) mPhoneDevice)
-                        .updateSummary(mCurrentConnectedDevice == mPhoneDevice);
-            }
-        }
-
         @Override
         public void onDeviceRemoved(MediaDevice device) {
             if (mMediaDevices.contains(device)) {
                 mMediaDevices.remove(device);
-                removePhoneMediaDeviceIfNecessary();
                 dispatchDeviceListUpdate();
             }
         }
@@ -295,7 +300,6 @@
         @Override
         public void onDeviceListRemoved(List<MediaDevice> devices) {
             mMediaDevices.removeAll(devices);
-            removePhoneMediaDeviceIfNecessary();
             dispatchDeviceListUpdate();
         }
 
@@ -308,7 +312,6 @@
                 return;
             }
             mCurrentConnectedDevice = connectDevice;
-            updatePhoneMediaDeviceSummary();
             dispatchDeviceAttributesChanged();
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 839d528..580e086 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -17,9 +17,12 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -40,14 +43,23 @@
         int TYPE_BLUETOOTH_DEVICE = 3;
     }
 
+    @VisibleForTesting
+    int mType;
+
     private int mConnectedRecord;
 
-    protected Context mContext;
-    protected int mType;
+    protected final Context mContext;
+    protected final MediaRoute2Info mRouteInfo;
+    protected final MediaRouter2Manager mRouterManager;
+    protected final String mPackageName;
 
-    MediaDevice(Context context, @MediaDeviceType int type) {
+    MediaDevice(Context context, @MediaDeviceType int type, MediaRouter2Manager routerManager,
+            MediaRoute2Info info, String packageName) {
         mType = type;
         mContext = context;
+        mRouteInfo = info;
+        mRouterManager = routerManager;
+        mPackageName = packageName;
     }
 
     void initDeviceRecord() {
@@ -83,13 +95,6 @@
      */
     public abstract String getId();
 
-    /**
-     * Transfer MediaDevice for media
-     *
-     * @return result of transfer media
-     */
-    public abstract boolean connect();
-
     void setConnectedRecord() {
         mConnectedRecord++;
         ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
@@ -97,11 +102,6 @@
     }
 
     /**
-     * Stop transfer MediaDevice
-     */
-    public abstract void disconnect();
-
-    /**
      * According the MediaDevice type to check whether we are connected to this MediaDevice.
      *
      * @return Whether it is connected.
@@ -162,6 +162,23 @@
     }
 
     /**
+     * Transfer MediaDevice for media
+     *
+     * @return result of transfer media
+     */
+    public boolean connect() {
+        setConnectedRecord();
+        mRouterManager.selectRoute(mPackageName, mRouteInfo);
+        return true;
+    }
+
+    /**
+     * Stop transfer MediaDevice
+     */
+    public void disconnect() {
+    }
+
+    /**
      * Rules:
      * 1. If there is one of the connected devices identified as a carkit, this carkit will
      * be always on the top of the device list. Rule 2 and Rule 3 can’t overrule this rule.
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
index 248b118..f341bf1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaOutputSliceConstants.java
@@ -32,6 +32,16 @@
     public static final String KEY_REMOTE_MEDIA = "remote_media";
 
     /**
+     * Key for the {@link android.media.session.MediaSession.Token}.
+     */
+    public static final String KEY_MEDIA_SESSION_TOKEN = "key_media_session_token";
+
+    /**
+     * Key for the {@link android.media.RoutingSessionInfo#getId()}
+     */
+    public static final String KEY_SESSION_INFO_ID = "key_session_info_id";
+
+    /**
      * Activity Action: Show a settings dialog containing {@link MediaDevice} to transfer media.
      */
     public static final String ACTION_MEDIA_OUTPUT =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index af91c34..166fbaa 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -17,14 +17,11 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 
 import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.BluetoothUtils;
-import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
 /**
  * PhoneMediaDevice extends MediaDevice to represents Phone device.
@@ -35,15 +32,12 @@
 
     public static final String ID = "phone_media_device_id_1";
 
-    private LocalBluetoothProfileManager mProfileManager;
-    private LocalBluetoothManager mLocalBluetoothManager;
     private String mSummary = "";
 
-    PhoneMediaDevice(Context context, LocalBluetoothManager localBluetoothManager) {
-        super(context, MediaDeviceType.TYPE_PHONE_DEVICE);
+    PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+            String packageName) {
+        super(context, MediaDeviceType.TYPE_PHONE_DEVICE, routerManager, info, packageName);
 
-        mLocalBluetoothManager = localBluetoothManager;
-        mProfileManager = mLocalBluetoothManager.getProfileManager();
         initDeviceRecord();
     }
 
@@ -69,32 +63,6 @@
     }
 
     @Override
-    public boolean connect() {
-        final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
-        final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
-
-        // Some device may not have HearingAidProfile, consider all situation to set active device.
-        boolean isConnected = false;
-        if (hapProfile != null && a2dpProfile != null) {
-            isConnected = hapProfile.setActiveDevice(null) && a2dpProfile.setActiveDevice(null);
-        } else if (a2dpProfile != null) {
-            isConnected = a2dpProfile.setActiveDevice(null);
-        } else if (hapProfile != null) {
-            isConnected = hapProfile.setActiveDevice(null);
-        }
-        updateSummary(isConnected);
-        setConnectedRecord();
-
-        Log.d(TAG, "connect() device : " + getName() + ", is selected : " + isConnected);
-        return isConnected;
-    }
-
-    @Override
-    public void disconnect() {
-        updateSummary(false);
-    }
-
-    @Override
     public boolean isConnected() {
         return true;
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 1182945..6307caf 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -31,6 +31,7 @@
 import android.content.res.Resources;
 import android.location.LocationManager;
 import android.media.AudioManager;
+import android.os.BatteryManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -122,12 +123,12 @@
     public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
         Resources resources = mock(Resources.class);
         when(resources.getInteger(
-                        eq(
-                                com.android
-                                        .internal
-                                        .R
-                                        .integer
-                                        .config_storageManagerDaystoRetainDefault)))
+                eq(
+                        com.android
+                                .internal
+                                .R
+                                .integer
+                                .config_storageManagerDaystoRetainDefault)))
                 .thenReturn(60);
         assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
     }
@@ -147,7 +148,8 @@
         private static Map<String, Integer> map = new HashMap<>();
 
         @Implementation
-        public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) {
+        public static boolean putIntForUser(ContentResolver cr, String name, int value,
+                int userHandle) {
             map.put(name, value);
             return true;
         }
@@ -312,4 +314,33 @@
         assertThat(Utils.getCombinedServiceState(mServiceState)).isEqualTo(
                 ServiceState.STATE_OUT_OF_SERVICE);
     }
+
+    @Test
+    public void getBatteryStatus_statusIsFull_returnFullString() {
+        final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+                resources.getString(R.string.battery_info_status_full));
+    }
+
+    @Test
+    public void getBatteryStatus_batteryLevelIs100_returnFullString() {
+        final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
+                BatteryManager.BATTERY_STATUS_FULL);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+                resources.getString(R.string.battery_info_status_full));
+    }
+
+    @Test
+    public void getBatteryStatus_batteryLevel99_returnChargingString() {
+        final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS,
+                BatteryManager.BATTERY_STATUS_CHARGING);
+        final Resources resources = mContext.getResources();
+
+        assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo(
+                resources.getString(R.string.battery_info_status_charging));
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index e0e2fd6..a39bcb7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -51,21 +51,7 @@
         when(mDevice.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
         when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
 
-        mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice);
-    }
-
-    @Test
-    public void connect_setActiveSuccess_isConnectedReturnTrue() {
-        when(mDevice.setActive()).thenReturn(true);
-
-        assertThat(mBluetoothMediaDevice.connect()).isTrue();
-    }
-
-    @Test
-    public void connect_setActiveFail_isConnectedReturnFalse() {
-        when(mDevice.setActive()).thenReturn(false);
-
-        assertThat(mBluetoothMediaDevice.connect()).isFalse();
+        mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index c9db0d1..04ceb21 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -18,10 +18,12 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageStats;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 
@@ -34,11 +36,14 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPackageManager;
 
 @RunWith(RobolectricTestRunner.class)
 public class InfoMediaDeviceTest {
 
     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
+    private static final String TEST_PACKAGE_NAME2 = "com.test.packagename2";
     private static final String TEST_ID = "test_id";
     private static final String TEST_NAME = "test_name";
 
@@ -50,11 +55,24 @@
 
     private Context mContext;
     private InfoMediaDevice mInfoMediaDevice;
+    private ShadowPackageManager mShadowPackageManager;
+    private ApplicationInfo mAppInfo;
+    private PackageInfo mPackageInfo;
+    private PackageStats mPackageStats;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
+        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+        mAppInfo = new ApplicationInfo();
+        mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+        mAppInfo.packageName = TEST_PACKAGE_NAME;
+        mAppInfo.name = TEST_NAME;
+        mPackageInfo = new PackageInfo();
+        mPackageInfo.packageName = TEST_PACKAGE_NAME;
+        mPackageInfo.applicationInfo = mAppInfo;
+        mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
 
         mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
                 TEST_PACKAGE_NAME);
@@ -90,9 +108,30 @@
     }
 
     @Test
-    public void connect_shouldSelectRoute() {
-        mInfoMediaDevice.connect();
+    public void getClientPackageName_returnPackageName() {
+        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
 
-        verify(mRouterManager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo);
+        assertThat(mInfoMediaDevice.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+    }
+
+    @Test
+    public void getClientAppLabel_matchedPackageName_returnLabel() {
+        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
+                mContext.getResources().getString(R.string.unknown));
+
+        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+
+        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(TEST_NAME);
+    }
+
+    @Test
+    public void getClientAppLabel_noMatchedPackageName_returnDefault() {
+        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
+
+        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
+                mContext.getResources().getString(R.string.unknown));
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 3726fb2..05f5fa0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -25,6 +25,9 @@
 import android.content.Context;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
+
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -45,6 +48,8 @@
 
     @Mock
     private MediaRouter2Manager mRouterManager;
+    @Mock
+    private LocalBluetoothManager mLocalBluetoothManager;
 
     private InfoMediaManager mInfoMediaManager;
     private Context mContext;
@@ -54,7 +59,8 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
 
-        mInfoMediaManager = new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null);
+        mInfoMediaManager =
+                new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null, mLocalBluetoothManager);
         mInfoMediaManager.mRouterManager = mRouterManager;
     }
 
@@ -142,4 +148,59 @@
 
         assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
     }
+
+    @Test
+    public void onRoutesChanged_getAvailableRoutes_shouldAddMediaDevice() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        when(info.getId()).thenReturn(TEST_ID);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        final List<MediaRoute2Info> routes = new ArrayList<>();
+        routes.add(info);
+        when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+
+        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+        assertThat(mediaDevice).isNull();
+
+        mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+        assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+    }
+
+    @Test
+    public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        when(info.getId()).thenReturn(TEST_ID);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        final List<MediaRoute2Info> routes = new ArrayList<>();
+        routes.add(info);
+        when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+        assertThat(mediaDevice).isNull();
+
+        mInfoMediaManager.mPackageName = "";
+        mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+    }
+
+    @Test
+    public void connectDeviceWithoutPackageName_noSession_returnFalse() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, info,
+                TEST_PACKAGE_NAME);
+
+        final List<RoutingSessionInfo> infos = new ArrayList<>();
+
+        when(mRouterManager.getActiveSessions()).thenReturn(infos);
+
+        assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index c780a64..3d67ba0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -80,7 +80,7 @@
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
 
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
-                mBluetoothMediaManager, mInfoMediaManager);
+                mInfoMediaManager, "com.test.packagename");
     }
 
     @Test
@@ -163,14 +163,14 @@
     }
 
     @Test
-    public void onDeviceAdded_mediaDeviceAndPhoneDeviceNotExistInList_addBothDevice() {
+    public void onDeviceAdded_addDevice() {
         final MediaDevice device = mock(MediaDevice.class);
 
         assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceAdded(device);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
         verify(mCallback).onDeviceListUpdate(any());
     }
 
@@ -206,7 +206,7 @@
     }
 
     @Test
-    public void onDeviceListAdded_phoneDeviceNotExistInList_addPhoneDeviceAndDevicesList() {
+    public void onDeviceListAdded_addDevicesList() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
@@ -220,12 +220,12 @@
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
         verify(mCallback).onDeviceListUpdate(any());
     }
 
     @Test
-    public void onDeviceListAdded_phoneDeviceExistInList_addDeviceList() {
+    public void onDeviceListAdded_addDeviceList() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
@@ -245,12 +245,12 @@
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(4);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
         verify(mCallback).onDeviceListUpdate(any());
     }
 
     @Test
-    public void onDeviceRemoved_phoneDeviceIsLastDeviceAfterRemoveMediaDevice_removeBothDevice() {
+    public void onDeviceRemoved_removeDevice() {
         final MediaDevice device1 = mock(MediaDevice.class);
         mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         mLocalMediaManager.mMediaDevices.add(device1);
@@ -260,7 +260,7 @@
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceRemoved(device1);
 
-        assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
         verify(mCallback).onDeviceListUpdate(any());
     }
 
@@ -298,7 +298,7 @@
     }
 
     @Test
-    public void onDeviceListRemoved_phoneDeviceIsLastDeviceAfterRemoveDeviceList_removeAll() {
+    public void onDeviceListRemoved_removeAll() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
@@ -311,7 +311,8 @@
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
         mLocalMediaManager.registerCallback(mCallback);
-        mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
+        mLocalMediaManager.mMediaDeviceCallback
+                .onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
 
         assertThat(mLocalMediaManager.mMediaDevices).isEmpty();
         verify(mCallback).onDeviceListUpdate(any());
@@ -384,4 +385,38 @@
 
         verify(mCallback).onDeviceAttributesChanged();
     }
+
+    @Test
+    public void getActiveMediaDevice_checkList() {
+        final List<MediaDevice> devices = new ArrayList<>();
+        final MediaDevice device1 = mock(MediaDevice.class);
+        final MediaDevice device2 = mock(MediaDevice.class);
+        final MediaDevice device3 = mock(MediaDevice.class);
+        device1.mType = MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE;
+        device2.mType = MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE;
+        device3.mType = MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
+        when(device1.getClientPackageName()).thenReturn(TEST_DEVICE_ID_1);
+        when(device2.getClientPackageName()).thenReturn(TEST_DEVICE_ID_2);
+        when(device3.getClientPackageName()).thenReturn(TEST_DEVICE_ID_3);
+        when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
+        when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
+        devices.add(device1);
+        devices.add(device2);
+        devices.add(device3);
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+        List<MediaDevice> activeDevices = mLocalMediaManager.getActiveMediaDevice(
+                MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
+        assertThat(activeDevices).containsExactly(device1);
+
+        activeDevices = mLocalMediaManager.getActiveMediaDevice(
+                MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
+        assertThat(activeDevices).containsExactly(device2);
+
+        activeDevices = mLocalMediaManager.getActiveMediaDevice(
+                MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
+        assertThat(activeDevices).containsExactly(device3);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index 02cb83e..fb8b78b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothClass;
@@ -83,6 +84,14 @@
     @Mock
     private MediaRoute2Info mRouteInfo3;
     @Mock
+    private MediaRoute2Info mBluetoothRouteInfo1;
+    @Mock
+    private MediaRoute2Info mBluetoothRouteInfo2;
+    @Mock
+    private MediaRoute2Info mBluetoothRouteInfo3;
+    @Mock
+    private MediaRoute2Info mPhoneRouteInfo;
+    @Mock
     private LocalBluetoothProfileManager mProfileManager;
     @Mock
     private HearingAidProfile mHapProfile;
@@ -90,6 +99,8 @@
     private A2dpProfile mA2dpProfile;
     @Mock
     private BluetoothDevice mDevice;
+    @Mock
+    private MediaRouter2Manager mMediaRouter2Manager;
 
     private BluetoothMediaDevice mBluetoothMediaDevice1;
     private BluetoothMediaDevice mBluetoothMediaDevice2;
@@ -100,7 +111,6 @@
     private InfoMediaDevice mInfoMediaDevice3;
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private PhoneMediaDevice mPhoneMediaDevice;
-    private MediaRouter2Manager mMediaRouter2Manager;
 
     @Before
     public void setUp() {
@@ -133,17 +143,24 @@
         when(mProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
         when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
 
-        mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
-        mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
-        mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
-        mMediaRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+        mBluetoothMediaDevice1 =
+                new BluetoothMediaDevice(mContext, mCachedDevice1, mMediaRouter2Manager,
+                        mBluetoothRouteInfo1, TEST_PACKAGE_NAME);
+        mBluetoothMediaDevice2 =
+                new BluetoothMediaDevice(mContext, mCachedDevice2, mMediaRouter2Manager,
+                        mBluetoothRouteInfo2, TEST_PACKAGE_NAME);
+        mBluetoothMediaDevice3 =
+                new BluetoothMediaDevice(mContext, mCachedDevice3, mMediaRouter2Manager,
+                        mBluetoothRouteInfo3, TEST_PACKAGE_NAME);
         mInfoMediaDevice1 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo1,
                 TEST_PACKAGE_NAME);
         mInfoMediaDevice2 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo2,
                 TEST_PACKAGE_NAME);
         mInfoMediaDevice3 = new InfoMediaDevice(mContext, mMediaRouter2Manager, mRouteInfo3,
                 TEST_PACKAGE_NAME);
-        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
+        mPhoneMediaDevice =
+                new PhoneMediaDevice(mContext, mMediaRouter2Manager, mPhoneRouteInfo,
+                        TEST_PACKAGE_NAME);
     }
 
     @Test
@@ -370,4 +387,11 @@
         assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice1);
         assertThat(mMediaDevices.get(6)).isEqualTo(mBluetoothMediaDevice2);
     }
+
+    @Test
+    public void connect_shouldSelectRoute() {
+        mInfoMediaDevice1.connect();
+
+        verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 0752dc0..db984fb 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -18,21 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.when;
-
-import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 
 import com.android.settingslib.R;
-import com.android.settingslib.bluetooth.A2dpProfile;
-import com.android.settingslib.bluetooth.HearingAidProfile;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
@@ -40,17 +32,6 @@
 @RunWith(RobolectricTestRunner.class)
 public class PhoneMediaDeviceTest {
 
-    @Mock
-    private LocalBluetoothProfileManager mLocalProfileManager;
-    @Mock
-    private LocalBluetoothManager mLocalBluetoothManager;
-    @Mock
-    private HearingAidProfile mHapProfile;
-    @Mock
-    private A2dpProfile mA2dpProfile;
-    @Mock
-    private BluetoothDevice mDevice;
-
     private Context mContext;
     private PhoneMediaDevice mPhoneMediaDevice;
 
@@ -59,68 +40,8 @@
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
 
-        when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
-        when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
-        when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHapProfile);
-        when(mA2dpProfile.getActiveDevice()).thenReturn(mDevice);
-
-        mPhoneMediaDevice = new PhoneMediaDevice(mContext, mLocalBluetoothManager);
-    }
-
-    @Test
-    public void connect_phoneDeviceSetActiveSuccess_isConnectedReturnTrue() {
-        when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
-        when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-
-        assertThat(mPhoneMediaDevice.connect()).isTrue();
-    }
-
-    @Test
-    public void connect_a2dpProfileSetActiveFail_isConnectedReturnFalse() {
-        when(mA2dpProfile.setActiveDevice(null)).thenReturn(false);
-        when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-
-        assertThat(mPhoneMediaDevice.connect()).isFalse();
-    }
-
-    @Test
-    public void connect_hearingAidProfileSetActiveFail_isConnectedReturnFalse() {
-        when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
-        when(mHapProfile.setActiveDevice(null)).thenReturn(false);
-
-        assertThat(mPhoneMediaDevice.connect()).isFalse();
-    }
-
-    @Test
-    public void connect_hearingAidAndA2dpProfileSetActiveFail_isConnectedReturnFalse() {
-        when(mA2dpProfile.setActiveDevice(null)).thenReturn(false);
-        when(mHapProfile.setActiveDevice(null)).thenReturn(false);
-
-        assertThat(mPhoneMediaDevice.connect()).isFalse();
-    }
-
-    @Test
-    public void connect_hearingAidProfileIsNullAndA2dpProfileNotNull_isConnectedReturnTrue() {
-        when(mLocalProfileManager.getHearingAidProfile()).thenReturn(null);
-
-        when(mA2dpProfile.setActiveDevice(null)).thenReturn(true);
-        assertThat(mPhoneMediaDevice.connect()).isTrue();
-    }
-
-    @Test
-    public void connect_hearingAidProfileNotNullAndA2dpProfileIsNull_isConnectedReturnTrue() {
-        when(mLocalProfileManager.getA2dpProfile()).thenReturn(null);
-
-        when(mHapProfile.setActiveDevice(null)).thenReturn(true);
-        assertThat(mPhoneMediaDevice.connect()).isTrue();
-    }
-
-    @Test
-    public void connect_hearingAidProfileAndA2dpProfileIsNull_isConnectedReturnFalse() {
-        when(mLocalProfileManager.getA2dpProfile()).thenReturn(null);
-        when(mLocalProfileManager.getHearingAidProfile()).thenReturn(null);
-
-        assertThat(mPhoneMediaDevice.connect()).isFalse();
+        mPhoneMediaDevice =
+                new PhoneMediaDevice(mContext, null, null, null);
     }
 
     @Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1d679c7..5946f21 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -308,6 +308,22 @@
                   android:excludeFromRecents="true"
                   android:exported="false" />
 
+        <!--
+        The following is used as a no-op/null home activity when
+        no other MAIN/HOME activity is present (e.g., in CSI).
+        -->
+        <activity android:name=".NullHome"
+                  android:excludeFromRecents="true"
+                  android:label=""
+                  android:screenOrientation="nosensor">
+            <!-- The priority here is set to be lower than that for Settings -->
+            <intent-filter android:priority="-1100">
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.HOME" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <receiver
             android:name=".BugreportRequestedReceiver"
             android:permission="android.permission.TRIGGER_SHELL_BUGREPORT">
diff --git a/packages/Shell/res/layout/null_home_finishing_boot.xml b/packages/Shell/res/layout/null_home_finishing_boot.xml
new file mode 100644
index 0000000..5f9563a
--- /dev/null
+++ b/packages/Shell/res/layout/null_home_finishing_boot.xml
@@ -0,0 +1,44 @@
+<?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
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#80000000"
+    android:forceHasOverlappingRendering="false">
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_gravity="center"
+        android:layout_marginStart="16dp"
+        android:layout_marginEnd="16dp">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="40sp"
+            android:textColor="?android:attr/textColorPrimary"
+            android:text="@*android:string/android_start_title"/>
+        <ProgressBar
+            style="@android:style/Widget.Material.ProgressBar.Horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="12.75dp"
+            android:colorControlActivated="?android:attr/textColorPrimary"
+            android:indeterminate="true"/>
+    </LinearLayout>
+</FrameLayout>
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 48d405a..1814593 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -97,6 +97,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -193,9 +194,7 @@
      * <p>
      * Must be a path supported by its FileProvider.
      */
-    // TODO: use the same variable for both dir
-    private static final String SCREENSHOT_DIR = "bugreports";
-    private static final String BUGREPORT_DIR = "/bugreports";
+    private static final String BUGREPORT_DIR = "bugreports";
 
     private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
 
@@ -230,7 +229,7 @@
 
     private final BugreportInfoDialog mInfoDialog = new BugreportInfoDialog();
 
-    private File mScreenshotsDir;
+    private File mBugreportsDir;
 
     private BugreportManager mBugreportManager;
 
@@ -263,11 +262,12 @@
         mScreenshotHandler = new ScreenshotHandler("BugreportProgressServiceScreenshotThread");
         startSelfIntent = new Intent(this, this.getClass());
 
-        mScreenshotsDir = new File(getFilesDir(), SCREENSHOT_DIR);
-        if (!mScreenshotsDir.exists()) {
-            Log.i(TAG, "Creating directory " + mScreenshotsDir + " to store temporary screenshots");
-            if (!mScreenshotsDir.mkdir()) {
-                Log.w(TAG, "Could not create directory " + mScreenshotsDir);
+        mBugreportsDir = new File(getFilesDir(), BUGREPORT_DIR);
+        if (!mBugreportsDir.exists()) {
+            Log.i(TAG, "Creating directory " + mBugreportsDir
+                    + " to store bugreports and screenshots");
+            if (!mBugreportsDir.mkdir()) {
+                Log.w(TAG, "Could not create directory " + mBugreportsDir);
             }
         }
         final Configuration conf = mContext.getResources().getConfiguration();
@@ -372,7 +372,7 @@
         @Override
         public void onFinished() {
             mInfo.renameBugreportFile();
-            mInfo.renameScreenshots(mScreenshotsDir);
+            mInfo.renameScreenshots();
             synchronized (mLock) {
                 sendBugreportFinishedBroadcastLocked();
             }
@@ -406,7 +406,7 @@
                         mInfo.bugreportFile);
             } else {
                 trackInfoWithIdLocked();
-                cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE);
+                cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE, mBugreportsDir);
                 final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED);
                 intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath);
                 intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo));
@@ -418,7 +418,8 @@
 
     private static void sendRemoteBugreportFinishedBroadcast(Context context,
             String bugreportFileName, File bugreportFile) {
-        cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE);
+        cleanupOldFiles(REMOTE_BUGREPORT_FILES_AMOUNT, REMOTE_MIN_KEEP_AGE,
+                bugreportFile.getParentFile());
         final Intent intent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH);
         final Uri bugreportUri = getUri(context, bugreportFile);
         final String bugreportHash = generateFileHash(bugreportFileName);
@@ -468,12 +469,12 @@
         return fileHash;
     }
 
-    static void cleanupOldFiles(final int minCount, final long minAge) {
+    static void cleanupOldFiles(final int minCount, final long minAge, File bugreportsDir) {
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
                 try {
-                    FileUtils.deleteOlderFiles(new File(BUGREPORT_DIR), minCount, minAge);
+                    FileUtils.deleteOlderFiles(bugreportsDir, minCount, minAge);
                 } catch (RuntimeException e) {
                     Log.e(TAG, "RuntimeException deleting old files", e);
                 }
@@ -604,18 +605,25 @@
         String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
 
         BugreportInfo info = new BugreportInfo(mContext, baseName, name,
-                shareTitle, shareDescription, bugreportType);
+                shareTitle, shareDescription, bugreportType, mBugreportsDir);
+        ParcelFileDescriptor bugreportFd;
+        ParcelFileDescriptor screenshotFd;
 
-        ParcelFileDescriptor bugreportFd = info.createBugreportFd();
-        if (bugreportFd == null) {
-            Log.e(TAG, "Bugreport parcel file descriptor is null.");
-            return;
-        }
-        ParcelFileDescriptor screenshotFd = info.createScreenshotFd();
-        if (screenshotFd == null) {
-            Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
-            FileUtils.closeQuietly(bugreportFd);
-            info.bugreportFile.delete();
+        try {
+            bugreportFd = info.createAndGetBugreportFd();
+            if (bugreportFd == null) {
+                Log.e(TAG, "Bugreport parcel file descriptor is null.");
+                return;
+            }
+            screenshotFd = info.createAndGetDefaultScreenshotFd();
+            if (screenshotFd == null) {
+                Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
+                FileUtils.closeQuietly(bugreportFd);
+                info.bugreportFile.delete();
+                return;
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Error in generating bugreport files: ", e);
             return;
         }
         mBugreportManager = (BugreportManager) mContext.getSystemService(
@@ -639,21 +647,24 @@
         }
     }
 
-    private static ParcelFileDescriptor createReadWriteFile(File file) {
+    private static ParcelFileDescriptor getFd(File file) {
         try {
-            file.createNewFile();
-            file.setReadable(true, true);
-            file.setWritable(true, true);
-
-            ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
+            return ParcelFileDescriptor.open(file,
                     ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
-            return fd;
-        } catch (IOException e) {
+        } catch (FileNotFoundException e) {
             Log.i(TAG, "Error in generating bugreports: ", e);
         }
         return null;
     }
 
+    private static void createReadWriteFile(File file) throws IOException {
+        if (!file.exists()) {
+            file.createNewFile();
+            file.setReadable(true, true);
+            file.setWritable(true, true);
+        }
+    }
+
     /**
      * Updates the system notification for a given bugreport.
      */
@@ -874,7 +885,7 @@
             return;
         }
         final String screenshotPath =
-                new File(mScreenshotsDir, info.getPathNextScreenshot()).getAbsolutePath();
+                new File(mBugreportsDir, info.getPathNextScreenshot()).getAbsolutePath();
 
         Message.obtain(mScreenshotHandler, MSG_SCREENSHOT_REQUEST, id, UNUSED_ARG2, screenshotPath)
                 .sendToTarget();
@@ -921,7 +932,7 @@
             info.addScreenshot(screenshotFile);
             if (info.finished) {
                 Log.d(TAG, "Screenshot finished after bugreport; updating share notification");
-                info.renameScreenshots(mScreenshotsDir);
+                info.renameScreenshots();
                 sendBugreportNotification(info, mTakingScreenshot);
             }
             msg = mContext.getString(R.string.bugreport_screenshot_taken);
@@ -1030,11 +1041,10 @@
     /**
      * Build {@link Intent} that can be used to share the given bugreport.
      */
-    private static Intent buildSendIntent(Context context, BugreportInfo info,
-            File screenshotsDir) {
+    private static Intent buildSendIntent(Context context, BugreportInfo info) {
         // Rename files (if required) before sharing
         info.renameBugreportFile();
-        info.renameScreenshots(screenshotsDir);
+        info.renameScreenshots();
         // Files are kept on private storage, so turn into Uris that we can
         // grant temporary permissions for.
         final Uri bugreportUri;
@@ -1120,7 +1130,7 @@
 
         addDetailsToZipFile(info);
 
-        final Intent sendIntent = buildSendIntent(mContext, info, mScreenshotsDir);
+        final Intent sendIntent = buildSendIntent(mContext, info);
         if (sendIntent == null) {
             Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built");
             synchronized (mLock) {
@@ -1813,24 +1823,37 @@
          */
         BugreportInfo(Context context, String baseName, String name,
                 @Nullable String shareTitle, @Nullable String shareDescription,
-                @BugreportParams.BugreportMode int type) {
+                @BugreportParams.BugreportMode int type, File bugreportsDir) {
             this.context = context;
             this.name = this.initialName = name;
             this.shareTitle = shareTitle == null ? "" : shareTitle;
             this.shareDescription = shareDescription == null ? "" : shareDescription;
             this.type = type;
             this.baseName = baseName;
+            createBugreportFile(bugreportsDir);
+            createScreenshotFile(bugreportsDir);
         }
 
-        ParcelFileDescriptor createBugreportFd() {
-            bugreportFile = new File(BUGREPORT_DIR, getFileName(this, ".zip"));
-            return createReadWriteFile(bugreportFile);
+        void createBugreportFile(File bugreportsDir) {
+            bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
         }
 
-        ParcelFileDescriptor createScreenshotFd() {
-            File screenshotFile = new File(BUGREPORT_DIR, getScreenshotName("default"));
+        void createScreenshotFile(File bugreportsDir) {
+            File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
             addScreenshot(screenshotFile);
-            return createReadWriteFile(screenshotFile);
+        }
+
+        ParcelFileDescriptor createAndGetBugreportFd() throws IOException {
+            createReadWriteFile(bugreportFile);
+            return getFd(bugreportFile);
+        }
+
+        ParcelFileDescriptor createAndGetDefaultScreenshotFd() throws IOException {
+            if (screenshotFiles.isEmpty()) {
+                return null;
+            }
+            createReadWriteFile(screenshotFiles.get(0));
+            return getFd(screenshotFiles.get(0));
         }
 
         /**
@@ -1859,7 +1882,7 @@
          * Rename all screenshots files so that they contain the new {@code name} instead of the
          * {@code initialName} if user has changed it.
          */
-        void renameScreenshots(File screenshotDir) {
+        void renameScreenshots() {
             if (TextUtils.isEmpty(name)) {
                 return;
             }
@@ -1869,7 +1892,7 @@
                 final String newName = oldName.replaceFirst(initialName, name);
                 final File newFile;
                 if (!newName.equals(oldName)) {
-                    final File renamedFile = new File(screenshotDir, newName);
+                    final File renamedFile = new File(oldFile.getParentFile(), newName);
                     Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile);
                     newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile;
                 } else {
@@ -1889,7 +1912,8 @@
          * Rename bugreport file to include the name given by user via UI
          */
         void renameBugreportFile() {
-            File newBugreportFile = new File(BUGREPORT_DIR, getFileName(this, ".zip"));
+            File newBugreportFile = new File(bugreportFile.getParentFile(),
+                    getFileName(this, ".zip"));
             if (!newBugreportFile.getPath().equals(bugreportFile.getPath())) {
                 if (bugreportFile.renameTo(newBugreportFile)) {
                     bugreportFile = newBugreportFile;
diff --git a/packages/Shell/src/com/android/shell/NullHome.java b/packages/Shell/src/com/android/shell/NullHome.java
new file mode 100644
index 0000000..bd97561
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/NullHome.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.shell;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * This covers the fallback case where no launcher is available.
+ * Usually Settings.apk has one fallback home activity.
+ * Settings.apk, however, is not part of CSI, which needs to be
+ * standalone (bootable and testable).
+ */
+public class NullHome extends Activity {
+    private static final String TAG = "NullHome";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, "onCreate");
+        setContentView(R.layout.null_home_finishing_boot);
+    }
+
+    protected void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy");
+    }
+}
diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml
index bde6ed5..8d9d6ee 100644
--- a/packages/SystemUI/res-keyguard/values/config.xml
+++ b/packages/SystemUI/res-keyguard/values/config.xml
@@ -22,10 +22,4 @@
 
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
-
-    <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V -->
-    <integer name="config_chargingSlowlyThreshold">5000000</integer>
-
-    <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V  -->
-    <integer name="config_chargingFastThreshold">7500000</integer>
 </resources>
diff --git a/packages/SystemUI/res/drawable-nodpi/android_11_dial.xml b/packages/SystemUI/res/drawable-nodpi/android_11_dial.xml
new file mode 100644
index 0000000..73fd37f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/android_11_dial.xml
@@ -0,0 +1,63 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt"
+    android:width="108dp"
+    android:height="108dp"
+    android:viewportWidth="108"
+    android:viewportHeight="108">
+  <path
+      android:pathData="M77.773,51.064h-1.583c-0.217,0 -0.393,-0.176 -0.393,-0.393v-1.46c0,-0.217 0.176,-0.393 0.393,-0.393h3.466c0.217,0 0.393,0.176 0.393,0.393v9.921c0,0.217 -0.176,0.393 -0.393,0.393h-1.49c-0.217,0 -0.393,-0.176 -0.393,-0.393V51.064z"
+      android:fillColor="#F86734"/>
+  <path
+      android:pathData="M83.598,51.064h-1.583c-0.217,0 -0.393,-0.176 -0.393,-0.393v-1.46c0,-0.217 0.176,-0.393 0.393,-0.393h3.466c0.217,0 0.393,0.176 0.393,0.393v9.921c0,0.217 -0.176,0.393 -0.393,0.393h-1.49c-0.217,0 -0.393,-0.176 -0.393,-0.393V51.064z"
+      android:fillColor="#F86734"/>
+  <path
+      android:pathData="M70.044,75.974m-0.644,0a0.644,0.644 0,1 1,1.288 0a0.644,0.644 0,1 1,-1.288 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M56.896,80.985m-0.718,0a0.718,0.718 0,1 1,1.436 0a0.718,0.718 0,1 1,-1.436 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M43.408,78.881m-0.795,0a0.795,0.795 0,1 1,1.59 0a0.795,0.795 0,1 1,-1.59 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M32.419,70.115m-0.874,0a0.874,0.874 0,1 1,1.748 0a0.874,0.874 0,1 1,-1.748 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M27.306,56.992m-0.954,0a0.954,0.954 0,1 1,1.908 0a0.954,0.954 0,1 1,-1.908 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M29.313,43.489m-1.036,0a1.036,1.036 0,1 1,2.072 0a1.036,1.036 0,1 1,-2.072 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M37.988,32.445m-1.118,0a1.118,1.118 0,1 1,2.236 0a1.118,1.118 0,1 1,-2.236 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M51.137,27.064m-1.201,0a1.201,1.201 0,1 1,2.402 0a1.201,1.201 0,1 1,-2.402 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M64.553,28.868m-1.284,0a1.284,1.284 0,1 1,2.568 0a1.284,1.284 0,1 1,-2.568 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M75.522,37.652m-1.368,0a1.368,1.368 0,1 1,2.736 0a1.368,1.368 0,1 1,-2.736 0"
+      android:fillColor="#d7effe"/>
+  <path
+      android:pathData="M87.942,115.052l-47.557,-47.557l26.869,-26.87l47.557,47.558z">
+    <aapt:attr name="android:fillColor">
+      <gradient 
+          android:startY="56.087"
+          android:startX="55.8464"
+          android:endY="100.0297"
+          android:endX="99.7891"
+          android:type="linear">
+        <item android:offset="0" android:color="#3F000000"/>
+        <item android:offset="1" android:color="#00000000"/>
+      </gradient>
+    </aapt:attr>
+  </path>
+  <path
+      android:pathData="M53.928,54.17m-18.999,0a18.999,18.999 0,1 1,37.998 0a18.999,18.999 0,1 1,-37.998 0"
+      android:fillColor="#3ddc84"/>
+  <path
+      android:pathData="M66.353,54.17m-3.185,0a3.185,3.185 0,1 1,6.37 0a3.185,3.185 0,1 1,-6.37 0"
+      android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon.xml b/packages/SystemUI/res/drawable-nodpi/icon.xml
index 7a68c03..7f8d4fa 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon.xml
@@ -15,5 +15,5 @@
 -->
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@drawable/icon_bg"/>
-    <foreground android:drawable="@drawable/q"/>
+    <foreground android:drawable="@drawable/android_11_dial"/>
 </adaptive-icon>
diff --git a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
index 2a54dfa..31b2a7f 100644
--- a/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
+++ b/packages/SystemUI/res/drawable-nodpi/icon_bg.xml
@@ -14,5 +14,5 @@
     limitations under the License.
 -->
 <color xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="#77C360" />
+    android:color="#073042" />
 
diff --git a/packages/SystemUI/res/drawable-nodpi/q.xml b/packages/SystemUI/res/drawable-nodpi/q.xml
deleted file mode 100644
index 0f42d2e..0000000
--- a/packages/SystemUI/res/drawable-nodpi/q.xml
+++ /dev/null
@@ -1,40 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="108dp"
-        android:height="108dp"
-        android:viewportWidth="108.0"
-        android:viewportHeight="108.0">
-    <group
-        android:name="scale"
-        android:pivotX="54" android:pivotY="54"
-        android:scaleX="0.9"
-        android:scaleY="0.9">
-        <group
-            android:name="nudge"
-            android:translateX="24"
-            android:translateY="23.5">
-            <path
-                android:name="tail"
-                android:fillColor="#FFFFFF"
-                android:pathData="M21.749674,34.122784l-9.431964,9.529709l-6.31771,-6.2529106l15.736504,-15.899582l64.765724,65.16436l-6.3046494,6.266083z"/>
-            <path
-                android:name="counter"
-                android:fillColor="#FFFFFF"
-                android:pathData="M30,9.32352941 C41.6954418,9.32352941 51.1764706,18.8045582 51.1764706,30.5 C51.1764706,42.1954418 41.6954418,51.6764706 30,51.6764706 C18.3045582,51.6764706 8.82352941,42.1954418 8.82352941,30.5 C8.82352941,18.8045582 18.3045582,9.32352941 30,9.32352941 L30,9.32352941 Z M30,0.5 C13.4314575,0.5 -5.53805368e-15,13.9314575 -7.10542736e-15,30.5 C-1.02401747e-14,47.0685425 13.4314575,60.5 30,60.5 C46.5685425,60.5 60,47.0685425 60,30.5 C59.9805514,13.9395201 46.5604799,0.519448617 30,0.5 Z"/>
-        </group>
-    </group>
-</vector>
diff --git a/media/java/android/media/AudioDeviceAddress.aidl b/packages/SystemUI/res/layout/controls_icon.xml
similarity index 60%
copy from media/java/android/media/AudioDeviceAddress.aidl
copy to packages/SystemUI/res/layout/controls_icon.xml
index 6a1a7f7..cc46ced 100644
--- a/media/java/android/media/AudioDeviceAddress.aidl
+++ b/packages/SystemUI/res/layout/controls_icon.xml
@@ -1,4 +1,7 @@
-/* Copyright 2019, The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2020, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -12,7 +15,12 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
+-->
 
-package android.media;
-
-parcelable AudioDeviceAddress;
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="28dp"
+    android:layout_height="28dp"
+    android:scaleType="fitCenter"
+    android:layout_marginLeft="2dp"
+    android:layout_marginRight="2dp" />
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 096f1f4..3e0699d 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -1,18 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2020, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
 <merge
     xmlns:android="http://schemas.android.com/apk/res/android">
-  <TextView
-      android:id="@+id/controls_title"
-      android:text="@string/quick_controls_title"
+  <LinearLayout
+      android:id="@+id/controls_no_favorites_group"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
-      android:singleLine="true"
-      android:gravity="center"
-      android:textSize="25dp"
+      android:orientation="vertical"
       android:paddingTop="40dp"
       android:paddingBottom="40dp"
       android:layout_marginLeft="10dp"
       android:layout_marginRight="10dp"
-      android:textColor="@*android:color/foreground_material_dark"
-      android:fontFamily="@*android:string/config_headlineFontFamily"
-      android:background="@drawable/control_no_favorites_background"/>
+      android:background="@drawable/control_no_favorites_background">
+
+    <LinearLayout
+        android:id="@+id/controls_icon_row"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:orientation="horizontal"
+        android:paddingBottom="8dp" />
+
+    <TextView
+        android:id="@+id/controls_title"
+        android:text="@string/quick_controls_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:layout_gravity="center"
+        android:textSize="25sp"
+        android:textColor="@*android:color/foreground_material_dark"
+        android:fontFamily="@*android:string/config_headlineFontFamily" />
+  </LinearLayout>
 </merge>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d72ce5e..8de2df5 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -487,9 +487,6 @@
     <!-- Whether or not to add a "people" notifications section -->
     <bool name="config_usePeopleFiltering">false</bool>
 
-    <!-- Package name for controls plugin -->
-    <string name="config_controlsPluginPackageName" translatable="false">com.android.systemui.controls.panel</string>
-
     <!-- Defines the blacklist for system icons.  That is to say, the icons in the status bar that
          are part of the blacklist are never displayed. Each item in the blacklist must be a string
          defined in core/res/res/config.xml to properly blacklist the icon.
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c4fa4e5..7a3395c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1090,7 +1090,7 @@
 
     <!--  Blur radius on status bar window and power menu  -->
     <dimen name="min_window_blur_radius">1px</dimen>
-    <dimen name="max_window_blur_radius">250px</dimen>
+    <dimen name="max_window_blur_radius">150px</dimen>
 
     <!-- How much into a DisplayCutout's bounds we can go, on each side -->
     <dimen name="display_cutout_margin_consumption">0px</dimen>
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index b2423b9..85724a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -27,7 +27,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
@@ -47,7 +47,6 @@
     private Handler mHandler;
     private IKeyguardClient mClient;
     private KeyguardSecurityCallback mKeyguardCallback;
-    private SurfaceControl.Transaction mTransaction;
 
     private final ServiceConnection mConnection = new ServiceConnection() {
         @Override
@@ -84,13 +83,13 @@
         }
 
         @Override
-        public void onSurfaceControlCreated(@Nullable SurfaceControl remoteSurfaceControl) {
+        public void onRemoteContentReady(
+                @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) {
             if (mHandler != null) {
                 mHandler.removeCallbacksAndMessages(null);
             }
-            if (remoteSurfaceControl != null) {
-                mTransaction.reparent(remoteSurfaceControl, mView.getSurfaceControl())
-                    .apply();
+            if (surfacePackage != null) {
+                mView.setChildSurfacePackage(surfacePackage);
             } else {
                 dismiss(KeyguardUpdateMonitor.getCurrentUser());
             }
@@ -138,11 +137,10 @@
 
     public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
             KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
-            Handler handler, SurfaceControl.Transaction transaction) {
+            Handler handler) {
         mContext = context;
         mHandler = handler;
         mParent = parent;
-        mTransaction = transaction;
         mUpdateMonitor = updateMonitor;
         mKeyguardCallback = callback;
         mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 29c67ae..ba8a1a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -39,7 +39,6 @@
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.SurfaceControl;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -149,8 +148,7 @@
         mViewConfiguration = ViewConfiguration.get(context);
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
         mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this,
-                mUpdateMonitor, mCallback, new Handler(Looper.myLooper()),
-                new SurfaceControl.Transaction());
+                mUpdateMonitor, mCallback, new Handler(Looper.myLooper()));
     }
 
     public void setSecurityCallback(SecurityCallback callback) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index f61f585..f48210c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -42,7 +42,6 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.view.animation.Animation;
-import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -251,9 +250,9 @@
             SliceItem item = rc.getSliceItem();
             final Uri itemTag = item.getSlice().getUri();
             // Try to reuse the view if already exists in the layout
-            KeyguardSliceButton button = mRow.findViewWithTag(itemTag);
+            KeyguardSliceTextView button = mRow.findViewWithTag(itemTag);
             if (button == null) {
-                button = new KeyguardSliceButton(mContext);
+                button = new KeyguardSliceTextView(mContext);
                 button.setTextColor(blendedColor);
                 button.setTag(itemTag);
                 final int viewIndex = i - (mHasHeader ? 1 : 0);
@@ -316,8 +315,8 @@
         int childCount = mRow.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View v = mRow.getChildAt(i);
-            if (v instanceof Button) {
-                ((Button) v).setTextColor(blendedColor);
+            if (v instanceof TextView) {
+                ((TextView) v).setTextColor(blendedColor);
             }
         }
     }
@@ -500,8 +499,8 @@
             int childCount = getChildCount();
             for (int i = 0; i < childCount; i++) {
                 View child = getChildAt(i);
-                if (child instanceof KeyguardSliceButton) {
-                    ((KeyguardSliceButton) child).setMaxWidth(width / childCount);
+                if (child instanceof KeyguardSliceTextView) {
+                    ((KeyguardSliceTextView) child).setMaxWidth(width / childCount);
                 }
             }
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -527,13 +526,13 @@
      * Representation of an item that appears under the clock on main keyguard message.
      */
     @VisibleForTesting
-    static class KeyguardSliceButton extends Button implements
+    static class KeyguardSliceTextView extends TextView implements
             ConfigurationController.ConfigurationListener {
 
         @StyleRes
         private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary;
 
-        public KeyguardSliceButton(Context context) {
+        KeyguardSliceTextView(Context context) {
             super(context, null /* attrs */, 0 /* styleAttr */, sStyleId);
             onDensityOrFontScaleChanged();
             setEllipsize(TruncateAt.END);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 6a04583..e5f6d3c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -21,15 +21,7 @@
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.ACTION_USER_STOPPED;
 import static android.content.Intent.ACTION_USER_UNLOCKED;
-import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
-import static android.os.BatteryManager.BATTERY_STATUS_FULL;
 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
-import static android.os.BatteryManager.EXTRA_HEALTH;
-import static android.os.BatteryManager.EXTRA_LEVEL;
-import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
-import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE;
-import static android.os.BatteryManager.EXTRA_PLUGGED;
-import static android.os.BatteryManager.EXTRA_STATUS;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM;
 
@@ -67,7 +59,6 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.media.AudioManager;
-import android.os.BatteryManager;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IRemoteCallback;
@@ -94,6 +85,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.settingslib.WirelessUtils;
+import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.DumpController;
 import com.android.systemui.Dumpable;
@@ -1059,29 +1051,9 @@
                         MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone"));
                 mHandler.sendMessage(msg);
             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-                final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN);
-                final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
-                final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
-                final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
 
-                final int maxChargingMicroAmp = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
-                int maxChargingMicroVolt = intent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1);
-                final int maxChargingMicroWatt;
-
-                if (maxChargingMicroVolt <= 0) {
-                    maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT;
-                }
-                if (maxChargingMicroAmp > 0) {
-                    // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor
-                    // to maintain precision equally on both factors.
-                    maxChargingMicroWatt = (maxChargingMicroAmp / 1000)
-                            * (maxChargingMicroVolt / 1000);
-                } else {
-                    maxChargingMicroWatt = -1;
-                }
                 final Message msg = mHandler.obtainMessage(
-                        MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
-                                maxChargingMicroWatt));
+                        MSG_BATTERY_UPDATE, new BatteryStatus(intent));
                 mHandler.sendMessage(msg);
             } else if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) {
                 SimData args = SimData.fromIntent(intent);
@@ -1323,82 +1295,6 @@
         }
     }
 
-    public static class BatteryStatus {
-        public static final int CHARGING_UNKNOWN = -1;
-        public static final int CHARGING_SLOWLY = 0;
-        public static final int CHARGING_REGULAR = 1;
-        public static final int CHARGING_FAST = 2;
-
-        public final int status;
-        public final int level;
-        public final int plugged;
-        public final int health;
-        public final int maxChargingWattage;
-
-        public BatteryStatus(int status, int level, int plugged, int health,
-                int maxChargingWattage) {
-            this.status = status;
-            this.level = level;
-            this.plugged = plugged;
-            this.health = health;
-            this.maxChargingWattage = maxChargingWattage;
-        }
-
-        /**
-         * Determine whether the device is plugged in (USB, power, or wireless).
-         *
-         * @return true if the device is plugged in.
-         */
-        public boolean isPluggedIn() {
-            return plugged == BatteryManager.BATTERY_PLUGGED_AC
-                    || plugged == BatteryManager.BATTERY_PLUGGED_USB
-                    || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS;
-        }
-
-        /**
-         * Determine whether the device is plugged in (USB, power).
-         *
-         * @return true if the device is plugged in wired (as opposed to wireless)
-         */
-        public boolean isPluggedInWired() {
-            return plugged == BatteryManager.BATTERY_PLUGGED_AC
-                    || plugged == BatteryManager.BATTERY_PLUGGED_USB;
-        }
-
-        /**
-         * Whether or not the device is charged. Note that some devices never return 100% for
-         * battery level, so this allows either battery level or status to determine if the
-         * battery is charged.
-         *
-         * @return true if the device is charged
-         */
-        public boolean isCharged() {
-            return status == BATTERY_STATUS_FULL || level >= 100;
-        }
-
-        /**
-         * Whether battery is low and needs to be charged.
-         *
-         * @return true if battery is low
-         */
-        public boolean isBatteryLow() {
-            return level < LOW_BATTERY_THRESHOLD;
-        }
-
-        public final int getChargingSpeed(int slowThreshold, int fastThreshold) {
-            return maxChargingWattage <= 0 ? CHARGING_UNKNOWN :
-                    maxChargingWattage < slowThreshold ? CHARGING_SLOWLY :
-                            maxChargingWattage > fastThreshold ? CHARGING_FAST :
-                                    CHARGING_REGULAR;
-        }
-
-        @Override
-        public String toString() {
-            return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged
-                    + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}";
-        }
-    }
-
     public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
         private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
 
@@ -2640,9 +2536,9 @@
      */
     private boolean refreshSimState(int subId, int slotId) {
         final TelephonyManager tele =
-            (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         int state = (tele != null) ?
-            tele.getSimState(slotId) : TelephonyManager.SIM_STATE_UNKNOWN;
+                tele.getSimState(slotId) : TelephonyManager.SIM_STATE_UNKNOWN;
         SimData data = mSimDatas.get(subId);
         final boolean changed;
         if (data == null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 8e87b7a..49f72a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -23,6 +23,7 @@
 import android.telephony.TelephonyManager;
 import android.view.WindowManagerPolicyConstants;
 
+import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 
 import java.util.TimeZone;
@@ -42,7 +43,7 @@
      *
      * @param status current battery status
      */
-    public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { }
+    public void onRefreshBatteryInfo(BatteryStatus status) { }
 
     /**
      * Called once per minute or when the time changes.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 5c65977..8a492a8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -87,7 +87,7 @@
     @VisibleForTesting @Nullable AuthBiometricView mBiometricView;
     @VisibleForTesting @Nullable AuthCredentialView mCredentialView;
 
-    private final ImageView mBackgroundView;
+    @VisibleForTesting final ImageView mBackgroundView;
     @VisibleForTesting final ScrollView mBiometricScrollView;
     private final View mPanelView;
 
@@ -333,6 +333,12 @@
                 throw new IllegalStateException("Unknown credential type: " + credentialType);
         }
 
+        // The background is used for detecting taps / cancelling authentication. Since the
+        // credential view is full-screen and should not be canceled from background taps,
+        // disable it.
+        mBackgroundView.setOnClickListener(null);
+        mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
         mCredentialView.setContainerView(this);
         mCredentialView.setEffectiveUserId(mEffectiveUserId);
         mCredentialView.setCredentialType(credentialType);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 2f1e4b4..05838ab 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.bubbles;
 
-import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
@@ -44,19 +43,13 @@
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager.RunningTaskInfo;
-import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.RemoteInput;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ShortcutManager;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.notification.NotificationListenerService.RankingMap;
@@ -75,25 +68,33 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ScreenshotHelper;
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.DumpController;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleController.BubbleExpandListener;
+import com.android.systemui.bubbles.BubbleController.BubbleStateChangeListener;
+import com.android.systemui.bubbles.BubbleController.NotifCallback;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
 import java.io.FileDescriptor;
@@ -101,10 +102,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.function.Consumer;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -116,7 +115,7 @@
  * The controller manages addition, removal, and visible state of bubbles on screen.
  */
 @Singleton
-public class BubbleController implements ConfigurationController.ConfigurationListener {
+public class BubbleController implements ConfigurationController.ConfigurationListener, Dumpable {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
 
@@ -140,14 +139,13 @@
 
     private final Context mContext;
     private final NotificationEntryManager mNotificationEntryManager;
+    private final NotifPipeline mNotifPipeline;
     private final BubbleTaskStackListener mTaskStackListener;
     private BubbleStateChangeListener mStateChangeListener;
     private BubbleExpandListener mExpandListener;
     @Nullable private BubbleStackView.SurfaceSynchronizer mSurfaceSynchronizer;
     private final NotificationGroupManager mNotificationGroupManager;
     private final ShadeController mShadeController;
-    private final RemoteInputUriController mRemoteInputUriController;
-    private Handler mHandler = new Handler() {};
 
     private BubbleData mBubbleData;
     @Nullable private BubbleStackView mStackView;
@@ -171,7 +169,6 @@
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final ZenModeController mZenModeController;
     private StatusBarStateListener mStatusBarStateListener;
-    private final ScreenshotHelper mScreenshotHelper;
 
     // Callback that updates BubbleOverflowActivity on data change.
     @Nullable private Runnable mOverflowCallback = null;
@@ -217,16 +214,6 @@
     }
 
     /**
-     * Listener for handling bubble screenshot events.
-     */
-    public interface BubbleScreenshotListener {
-        /**
-         * Called to trigger taking a screenshot and sending the result to a bubble.
-         */
-        void onBubbleScreenshot(Bubble bubble);
-    }
-
-    /**
      * Listener to be notified when a bubbles' notification suppression state changes.
      */
     public interface NotificationSuppressionChangedListener {
@@ -243,16 +230,17 @@
      */
     public interface NotifCallback {
         /**
-         * Called when the BubbleController wants to remove an entry that it was previously hiding
-         * from the shade. See {@link BubbleController#isBubbleNotificationSuppressedFromShade}.
+         * Called when a bubbled notification that was hidden from the shade is now being removed
+         * This can happen when an app cancels a bubbled notification or when the user dismisses a
+         * bubble.
          */
-        void removeNotification(NotificationEntry entry);
+        void removeNotification(NotificationEntry entry, int reason);
 
         /**
          * Called when a bubbled notification has changed whether it should be
          * filtered from the shade.
          */
-        void invalidateNotificationFilter(String reason);
+        void invalidateNotifications(String reason);
 
         /**
          * Called on a bubbled entry that has been removed when there are no longer
@@ -301,11 +289,13 @@
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
             NotificationEntryManager entryManager,
-            RemoteInputUriController remoteInputUriController) {
+            NotifPipeline notifPipeline,
+            FeatureFlags featureFlags,
+            DumpController dumpController) {
         this(context, notificationShadeWindowController, statusBarStateController, shadeController,
                 data, null /* synchronizer */, configurationController, interruptionStateProvider,
                 zenModeController, notifUserManager, groupManager, entryManager,
-                remoteInputUriController);
+                notifPipeline, featureFlags, dumpController);
     }
 
     public BubbleController(Context context,
@@ -320,13 +310,15 @@
             NotificationLockscreenUserManager notifUserManager,
             NotificationGroupManager groupManager,
             NotificationEntryManager entryManager,
-            RemoteInputUriController remoteInputUriController) {
+            NotifPipeline notifPipeline,
+            FeatureFlags featureFlags,
+            DumpController dumpController) {
+        dumpController.registerDumpable(TAG, this);
         mContext = context;
         mShadeController = shadeController;
         mNotificationInterruptionStateProvider = interruptionStateProvider;
         mNotifUserManager = notifUserManager;
         mZenModeController = zenModeController;
-        mRemoteInputUriController = remoteInputUriController;
         mZenModeController.addCallback(new ZenModeController.Callback() {
             @Override
             public void onZenChanged(int zen) {
@@ -364,7 +356,13 @@
 
         mNotificationEntryManager = entryManager;
         mNotificationGroupManager = groupManager;
-        setupNEM();
+        mNotifPipeline = notifPipeline;
+
+        if (!featureFlags.isNewNotifPipelineRenderingEnabled()) {
+            setupNEM();
+        } else {
+            setupNotifPipeline();
+        }
 
         mNotificationShadeWindowController = notificationShadeWindowController;
         mStatusBarStateListener = new StatusBarStateListener();
@@ -399,7 +397,6 @@
         mUserCreatedBubbles = new HashSet<>();
         mUserBlockedBubbles = new HashSet<>();
 
-        mScreenshotHelper = new ScreenshotHelper(context);
         mBubbleIconFactory = new BubbleIconFactory(context);
     }
 
@@ -424,6 +421,14 @@
                     }
 
                     @Override
+                    public void onEntryRemoved(
+                            NotificationEntry entry,
+                            @android.annotation.Nullable NotificationVisibility visibility,
+                            boolean removedByUser) {
+                        BubbleController.this.onEntryRemoved(entry);
+                    }
+
+                    @Override
                     public void onNotificationRankingUpdated(RankingMap rankingMap) {
                         onRankingUpdated(rankingMap);
                     }
@@ -433,8 +438,29 @@
                 new NotificationRemoveInterceptor() {
                     @Override
                     public boolean onNotificationRemoveRequested(
-                            String key, NotificationEntry entry, int reason) {
-                        return shouldInterceptDismissal(entry, reason);
+                            String key,
+                            NotificationEntry entry,
+                            int dismissReason) {
+                        final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
+                        final boolean isUserDimiss = dismissReason == REASON_CANCEL
+                                || dismissReason == REASON_CLICK;
+                        final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
+                                || dismissReason == REASON_APP_CANCEL_ALL;
+                        final boolean isSummaryCancel =
+                                dismissReason == REASON_GROUP_SUMMARY_CANCELED;
+
+                        // Need to check for !appCancel here because the notification may have
+                        // previously been dismissed & entry.isRowDismissed would still be true
+                        boolean userRemovedNotif =
+                                (entry != null && entry.isRowDismissed() && !isAppCancel)
+                                || isClearAll || isUserDimiss || isSummaryCancel;
+
+                        if (userRemovedNotif || isUserCreatedBubble(key)
+                                || isSummaryOfUserCreatedBubble(entry)) {
+                            return handleDismissalInterception(entry);
+                        }
+
+                        return false;
                     }
                 });
 
@@ -458,13 +484,13 @@
 
         addNotifCallback(new NotifCallback() {
             @Override
-            public void removeNotification(NotificationEntry entry) {
+            public void removeNotification(NotificationEntry entry, int reason) {
                 mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
-                        UNDEFINED_DISMISS_REASON);
+                        reason);
             }
 
             @Override
-            public void invalidateNotificationFilter(String reason) {
+            public void invalidateNotifications(String reason) {
                 mNotificationEntryManager.updateNotifications(reason);
             }
 
@@ -472,18 +498,28 @@
             public void maybeCancelSummary(NotificationEntry entry) {
                 // Check if removed bubble has an associated suppressed group summary that needs
                 // to be removed now.
-                final String groupKey = entry.getSbn().getGroup();
+                final String groupKey = entry.getSbn().getGroupKey();
                 if (mBubbleData.isSummarySuppressed(groupKey)) {
-                    mBubbleData.removeSuppressedSummary(entry.getSbn().getGroupKey());
+                    mBubbleData.removeSuppressedSummary(groupKey);
 
                     final NotificationEntry summary =
                             mNotificationEntryManager.getActiveNotificationUnfiltered(
                                     mBubbleData.getSummaryKey(groupKey));
-                    mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
-                            UNDEFINED_DISMISS_REASON);
+                    if (summary != null) {
+                        mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
+                                UNDEFINED_DISMISS_REASON);
+                    }
                 }
 
-                // Check if summary should be removed from NoManGroup
+                // Check if we still need to remove the summary from NoManGroup because the summary
+                // may not be in the mBubbleData.mSuppressedGroupKeys list and removed above.
+                // For example:
+                // 1. Bubbled notifications (group) is posted to shade and are visible bubbles
+                // 2. User expands bubbles so now their respective notifications in the shade are
+                // hidden, including the group summary
+                // 3. User removes all bubbles
+                // 4. We expect all the removed bubbles AND the summary (note: the summary was
+                // never added to the suppressedSummary list in BubbleData, so we add this check)
                 NotificationEntry summary =
                         mNotificationGroupManager.getLogicalGroupSummary(entry.getSbn());
                 if (summary != null) {
@@ -500,6 +536,31 @@
         });
     }
 
+    private void setupNotifPipeline() {
+        mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
+            @Override
+            public void onEntryAdded(NotificationEntry entry) {
+                BubbleController.this.onEntryAdded(entry);
+            }
+
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                BubbleController.this.onEntryUpdated(entry);
+            }
+
+            @Override
+            public void onRankingUpdate(RankingMap rankingMap) {
+                onRankingUpdated(rankingMap);
+            }
+
+            @Override
+            public void onEntryRemoved(NotificationEntry entry,
+                    @NotifCollection.CancellationReason int reason) {
+                BubbleController.this.onEntryRemoved(entry);
+            }
+        });
+    }
+
     /**
      * Sets whether to perform inflation on the same thread as the caller. This method should only
      * be used in tests, not in production.
@@ -536,9 +597,6 @@
             if (mExpandListener != null) {
                 mStackView.setExpandListener(mExpandListener);
             }
-            if (mBubbleScreenshotListener != null) {
-                mStackView.setBubbleScreenshotListener(mBubbleScreenshotListener);
-            }
         }
     }
 
@@ -783,7 +841,7 @@
             Log.d(TAG, "onUserDemotedBubble: " + entry.getKey());
         }
         entry.setFlagBubble(false);
-        removeBubble(entry.getKey(), DISMISS_BLOCKED);
+        removeBubble(entry, DISMISS_BLOCKED);
         mUserCreatedBubbles.remove(entry.getKey());
         if (BubbleExperimentConfig.isPackageWhitelistedToAutoBubble(
                 mContext, entry.getSbn().getPackageName())) {
@@ -800,17 +858,29 @@
         return mUserCreatedBubbles.contains(key);
     }
 
+    boolean isSummaryOfUserCreatedBubble(NotificationEntry entry) {
+        if (isSummaryOfBubbles(entry)) {
+            List<Bubble> bubbleChildren =
+                    mBubbleData.getBubblesInGroup(entry.getSbn().getGroupKey());
+            for (int i = 0; i < bubbleChildren.size(); i++) {
+                // Check if any are user-created (i.e. experimental bubbles)
+                if (isUserCreatedBubble(bubbleChildren.get(i).getKey())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
-     * Removes the bubble associated with the {@param uri}.
+     * Removes the bubble with the given NotificationEntry.
      * <p>
      * Must be called from the main thread.
      */
     @MainThread
-    void removeBubble(String key, int reason) {
-        // TEMP: refactor to change this to pass entry
-        Bubble bubble = mBubbleData.getBubbleWithKey(key);
-        if (bubble != null) {
-            mBubbleData.notificationEntryRemoved(bubble.getEntry(), reason);
+    void removeBubble(NotificationEntry entry, int reason) {
+        if (mBubbleData.hasBubbleWithKey(entry.getKey())) {
+            mBubbleData.notificationEntryRemoved(entry, reason);
         }
     }
 
@@ -840,7 +910,7 @@
                 && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
         if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
             // It was previously a bubble but no longer a bubble -- lets remove it
-            removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
+            removeBubble(entry, DISMISS_NO_LONGER_BUBBLE);
         } else if (shouldBubble) {
             if (wasAdjusted && !previouslyUserCreated) {
                 // Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
@@ -850,6 +920,21 @@
         }
     }
 
+    private void onEntryRemoved(NotificationEntry entry) {
+        if (isSummaryOfBubbles(entry)) {
+            final String groupKey = entry.getSbn().getGroupKey();
+            mBubbleData.removeSuppressedSummary(groupKey);
+
+            // Remove any associated bubble children with the summary
+            final List<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
+            for (int i = 0; i < bubbleChildren.size(); i++) {
+                removeBubble(bubbleChildren.get(i).getEntry(), DISMISS_GROUP_CANCELLED);
+            }
+        } else {
+            removeBubble(entry, DISMISS_NOTIF_CANCEL);
+        }
+    }
+
     private void onRankingUpdated(RankingMap rankingMap) {
         // Forward to BubbleData to block any bubbles which should no longer be shown
         mBubbleData.notificationRankingUpdated(rankingMap);
@@ -877,7 +962,6 @@
                 final Bubble bubble = removed.first;
                 @DismissReason final int reason = removed.second;
                 mStackView.removeBubble(bubble);
-
                 // If the bubble is removed for user switching, leave the notification in place.
                 if (reason != DISMISS_USER_CHANGED) {
                     if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
@@ -885,7 +969,7 @@
                         // The bubble is now gone & the notification is hidden from the shade, so
                         // time to actually remove it
                         for (NotifCallback cb : mCallbacks) {
-                            cb.removeNotification(bubble.getEntry());
+                            cb.removeNotification(bubble.getEntry(), REASON_CANCEL);
                         }
                     } else {
                         // Update the flag for SysUI
@@ -939,7 +1023,7 @@
             }
 
             for (NotifCallback cb : mCallbacks) {
-                cb.invalidateNotificationFilter("BubbleData.Listener.applyUpdate");
+                cb.invalidateNotifications("BubbleData.Listener.applyUpdate");
             }
             updateStack();
 
@@ -961,124 +1045,85 @@
     };
 
     /**
-     * We intercept notification entries cancelled by the user (i.e. dismissed) when there is an
-     * active bubble associated with it. We do this so that developers can still cancel it
-     * (and hence the bubbles associated with it). However, these intercepted notifications
-     * should then be hidden from the shade since the user has cancelled them, so we update
-     * {@link Bubble#showInShade}.
-     *
-     * The cancellation of summaries with children associated with bubbles are also handled in this
-     * method. User-cancelled summaries are tracked by {@link BubbleData#addSummaryToSuppress}.
+     * We intercept notification entries (including group summaries) dismissed by the user when
+     * there is an active bubble associated with it. We do this so that developers can still
+     * cancel it (and hence the bubbles associated with it). However, these intercepted
+     * notifications should then be hidden from the shade since the user has cancelled them, so we
+     *  {@link Bubble#setSuppressNotification}.  For the case of suppressed summaries, we also add
+     *  {@link BubbleData#addSummaryToSuppress}.
      *
      * @return true if we want to intercept the dismissal of the entry, else false.
      */
-    public boolean shouldInterceptDismissal(NotificationEntry entry, int dismissReason) {
+    public boolean handleDismissalInterception(NotificationEntry entry) {
         if (entry == null) {
             return false;
         }
-        String key = entry.getKey();
-        String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
-        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
 
-        boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
-        boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
-                && mBubbleData.getSummaryKey(groupKey).equals(key));
-        boolean isSummary = entry != null
-                && entry.getSbn().getNotification().isGroupSummary();
-        boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
-                && bubbleChildren != null && !bubbleChildren.isEmpty();
+        final boolean interceptBubbleDismissal = mBubbleData.hasBubbleWithKey(entry.getKey())
+                && entry.isBubble();
+        final boolean interceptSummaryDismissal = isSummaryOfBubbles(entry);
 
-        if (!inBubbleData && !isSummaryOfBubbles) {
-            return false;
-        }
-
-        final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
-        final boolean isUserDimiss = dismissReason == REASON_CANCEL
-                || dismissReason == REASON_CLICK;
-        final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
-                || dismissReason == REASON_APP_CANCEL_ALL;
-        final boolean isSummaryCancel = dismissReason == REASON_GROUP_SUMMARY_CANCELED;
-
-        // Need to check for !appCancel here because the notification may have
-        // previously been dismissed & entry.isRowDismissed would still be true
-        boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
-                || isClearAll || isUserDimiss || isSummaryCancel;
-        if (isSummaryOfBubbles) {
-            return handleSummaryRemovalInterception(entry, userRemovedNotif);
-        }
-
-        // The bubble notification sticks around in the data as long as the bubble is
-        // not dismissed and the app hasn't cancelled the notification.
-        Bubble bubble = mBubbleData.getBubbleWithKey(key);
-        boolean bubbleExtended = entry != null && entry.isBubble()
-                && (userRemovedNotif || isUserCreatedBubble(bubble.getKey()));
-        if (bubbleExtended) {
+        if (interceptSummaryDismissal) {
+            handleSummaryDismissalInterception(entry);
+        } else if (interceptBubbleDismissal) {
+            Bubble bubble = mBubbleData.getBubbleWithKey(entry.getKey());
             bubble.setSuppressNotification(true);
             bubble.setShowDot(false /* show */, true /* animate */);
-            for (NotifCallback cb : mCallbacks) {
-                cb.invalidateNotificationFilter("BubbleController"
-                        + ".shouldInterceptDismissal");
-            }
-            return true;
-        } else if (!userRemovedNotif && entry != null) {
-            // This wasn't a user removal so we should remove the bubble as well
-            mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
+        } else {
             return false;
         }
-        return false;
+
+        // Update the shade
+        for (NotifCallback cb : mCallbacks) {
+            cb.invalidateNotifications("BubbleController.handleDismissalInterception");
+        }
+        return true;
     }
 
-    private boolean handleSummaryRemovalInterception(NotificationEntry summary,
-            boolean userRemovedNotif) {
-        String groupKey = summary.getSbn().getGroupKey();
-        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
-
-        if (userRemovedNotif) {
-            // If it's a user dismiss we mark the children to be hidden from the shade.
-            for (int i = 0; i < bubbleChildren.size(); i++) {
-                Bubble bubbleChild = bubbleChildren.get(i);
-                // As far as group manager is concerned, once a child is no longer shown
-                // in the shade, it is essentially removed.
-                mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
-                bubbleChild.setSuppressNotification(true);
-                bubbleChild.setShowDot(false /* show */, true /* animate */);
-            }
-            // And since all children are removed, remove the summary.
-            mNotificationGroupManager.onEntryRemoved(summary);
-
-            // If the summary was auto-generated we don't need to keep that notification around
-            // because apps can't cancel it; so we only intercept & suppress real summaries.
-            boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
-                    & FLAG_AUTOGROUP_SUMMARY) != 0;
-            if (!isAutogroupSummary) {
-                // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
-                mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
-                        summary.getKey());
-                // Tell shade to update for the suppression
-                mNotificationEntryManager.updateNotifications("BubbleController"
-                        + ".handleSummaryRemovalInterception");
-            }
-            return !isAutogroupSummary;
-        } else {
-            // If it's not a user dismiss it's a cancel.
-            for (int i = 0; i < bubbleChildren.size(); i++) {
-                // First check if any of these are user-created (i.e. experimental bubbles)
-                if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
-                    // Experimental bubble! Intercept the removal.
-                    return true;
-                }
-            }
-
-            // Not an experimental bubble, safe to remove.
-            mBubbleData.removeSuppressedSummary(groupKey);
-            // Remove any associated bubble children with the summary.
-            for (int i = 0; i < bubbleChildren.size(); i++) {
-                Bubble bubbleChild = bubbleChildren.get(i);
-                mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
-                        DISMISS_GROUP_CANCELLED);
-            }
+    private boolean isSummaryOfBubbles(NotificationEntry entry) {
+        if (entry == null) {
             return false;
         }
+
+        String groupKey = entry.getSbn().getGroupKey();
+        ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
+        boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
+                && mBubbleData.getSummaryKey(groupKey).equals(entry.getKey()));
+        boolean isSummary = entry.getSbn().getNotification().isGroupSummary();
+        return (isSuppressedSummary || isSummary)
+                && bubbleChildren != null
+                && !bubbleChildren.isEmpty();
+    }
+
+    private void handleSummaryDismissalInterception(NotificationEntry summary) {
+        // current children in the row:
+        final List<NotificationEntry> children = summary.getChildren();
+        if (children != null) {
+            for (int i = 0; i < children.size(); i++) {
+                NotificationEntry child = children.get(i);
+                if (mBubbleData.hasBubbleWithKey(child.getKey())) {
+                    // Suppress the bubbled child
+                    // As far as group manager is concerned, once a child is no longer shown
+                    // in the shade, it is essentially removed.
+                    Bubble bubbleChild = mBubbleData.getBubbleWithKey(child.getKey());
+                    mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
+                    bubbleChild.setSuppressNotification(true);
+                    bubbleChild.setShowDot(false /* show */, true /* animate */);
+                } else {
+                    // non-bubbled children can be removed
+                    for (NotifCallback cb : mCallbacks) {
+                        cb.removeNotification(child, REASON_GROUP_SUMMARY_CANCELED);
+                    }
+                }
+            }
+        }
+
+        // And since all children are removed, remove the summary.
+        mNotificationGroupManager.onEntryRemoved(summary);
+
+        // TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
+        mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
+                summary.getKey());
     }
 
     /**
@@ -1267,68 +1312,4 @@
             }
         }
     }
-
-    // TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic.
-    private Intent prepareRemoteInputFromData(String contentType, Uri data,
-            RemoteInput remoteInput, NotificationEntry entry) {
-        HashMap<String, Uri> results = new HashMap<>();
-        results.put(contentType, data);
-        mRemoteInputUriController.grantInlineReplyUriPermission(entry.getSbn(), data);
-        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        RemoteInput.addDataResultToIntent(remoteInput, fillInIntent, results);
-
-        return fillInIntent;
-    }
-
-    // TODO: Copied from RemoteInputView. Consolidate RemoteInput intent logic.
-    private void sendRemoteInput(Intent intent, NotificationEntry entry,
-            PendingIntent pendingIntent) {
-        // Tell ShortcutManager that this package has been "activated".  ShortcutManager
-        // will reset the throttling for this package.
-        // Strictly speaking, the intent receiver may be different from the notification publisher,
-        // but that's an edge case, and also because we can't always know which package will receive
-        // an intent, so we just reset for the publisher.
-        mContext.getSystemService(ShortcutManager.class).onApplicationActive(
-                entry.getSbn().getPackageName(),
-                entry.getSbn().getUser().getIdentifier());
-
-        try {
-            pendingIntent.send(mContext, 0, intent);
-        } catch (PendingIntent.CanceledException e) {
-            Log.i(TAG, "Unable to send remote input result", e);
-        }
-    }
-
-    private void sendScreenshotToBubble(Bubble bubble) {
-        mScreenshotHelper.takeScreenshot(
-                android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN,
-                true /* hasStatus */,
-                true /* hasNav */,
-                mHandler,
-                new Consumer<Uri>() {
-                    @Override
-                    public void accept(Uri uri) {
-                        if (uri != null) {
-                            NotificationEntry entry = bubble.getEntry();
-                            Pair<RemoteInput, Notification.Action> pair = entry.getSbn()
-                                    .getNotification().findRemoteInputActionPair(false);
-                            if (pair != null) {
-                                RemoteInput remoteInput = pair.first;
-                                Notification.Action action = pair.second;
-                                Intent dataIntent = prepareRemoteInputFromData("image/png", uri,
-                                        remoteInput, entry);
-                                sendRemoteInput(dataIntent, entry, action.actionIntent);
-                                mBubbleData.setSelectedBubble(bubble);
-                                mBubbleData.setExpanded(true);
-                            } else {
-                                Log.w(TAG, "No RemoteInput found for notification: "
-                                        + entry.getSbn().getKey());
-                            }
-                        }
-                    }
-                });
-    }
-
-    private final BubbleScreenshotListener mBubbleScreenshotListener =
-            bubble -> sendScreenshotToBubble(bubble);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 50a5063..0d5261d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -19,9 +19,9 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.view.Display.INVALID_DISPLAY;
-
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
 import static android.view.ViewRootImpl.sNewInsetsMode;
+
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_EXPANDED_VIEW;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -56,6 +56,7 @@
 import com.android.systemui.recents.TriangleShape;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.AlphaOptimizedButton;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 /**
  * Container for the expanded bubble view, handles rendering the caret and settings icon.
@@ -146,7 +147,7 @@
                             // the bubble again so we'll just remove it.
                             Log.w(TAG, "Exception while displaying bubble: " + getBubbleKey()
                                     + ", " + e.getMessage() + "; removing bubble");
-                            mBubbleController.removeBubble(getBubbleKey(),
+                            mBubbleController.removeBubble(getBubbleEntry(),
                                     BubbleController.DISMISS_INVALID_INTENT);
                         }
                     });
@@ -190,7 +191,7 @@
             }
             if (mBubble != null && !mBubbleController.isUserCreatedBubble(mBubble.getKey())) {
                 // Must post because this is called from a binder thread.
-                post(() -> mBubbleController.removeBubble(mBubble.getKey(),
+                post(() -> mBubbleController.removeBubble(mBubble.getEntry(),
                         BubbleController.DISMISS_TASK_FINISHED));
             }
         }
@@ -279,6 +280,10 @@
         return mBubble != null ? mBubble.getKey() : "null";
     }
 
+    private NotificationEntry getBubbleEntry() {
+        return mBubble != null ? mBubble.getEntry() : null;
+    }
+
     void applyThemeAttrs() {
         final TypedArray ta = mContext.obtainStyledAttributes(
                 new int[] {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 006de84..20b3386 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -73,9 +73,6 @@
 
     private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
 
-    private static final String ALLOW_BUBBLE_MENU = "allow_bubble_screenshot_menu";
-    private static final boolean ALLOW_BUBBLE_MENU_DEFAULT = false;
-
     private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
     private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = false;
 
@@ -137,16 +134,6 @@
      * When true, show a menu when a bubble is long-pressed, which will allow the user to take
      * actions on that bubble.
      */
-    static boolean allowBubbleScreenshotMenu(Context context) {
-        return Settings.Secure.getInt(context.getContentResolver(),
-                ALLOW_BUBBLE_MENU,
-                ALLOW_BUBBLE_MENU_DEFAULT ? 1 : 0) != 0;
-    }
-
-    /**
-     * When true, show a menu when a bubble is long-pressed, which will allow the user to take
-     * actions on that bubble.
-     */
     static boolean allowBubbleOverflow(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 ALLOW_BUBBLE_OVERFLOW,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java
deleted file mode 100644
index bf83065..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleMenuView.java
+++ /dev/null
@@ -1,84 +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.bubbles;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Menu which allows users to take actions on bubbles, ex. screenshots.
- */
-public class BubbleMenuView extends FrameLayout {
-    private FrameLayout mMenu;
-    private boolean mShowing = false;
-
-    /** Delay before taking a screenshot once the button is tapped to allow the menu time to hide.*/
-    public static final long SCREENSHOT_DELAY = 200;
-
-    public BubbleMenuView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public BubbleMenuView(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mMenu = findViewById(R.id.bubble_menu_view);
-        ImageView icon = findViewById(com.android.internal.R.id.icon);
-        icon.setImageDrawable(mContext.getDrawable(com.android.internal.R.drawable.ic_screenshot));
-    }
-
-    /**
-     * Get the bubble menu view.
-     */
-    public View getMenuView() {
-        return mMenu;
-    }
-
-    /**
-     * Checks whether the bubble menu is currently displayed.
-     */
-    public boolean isShowing() {
-        return mShowing;
-    }
-
-    /**
-     * Show the bubble menu at the specified position on the screen.
-     */
-    public void show(float x, float y) {
-        mShowing = true;
-        this.setVisibility(VISIBLE);
-        mMenu.setTranslationX(x);
-        mMenu.setTranslationY(y);
-    }
-
-    /**
-     * Hide the bubble menu.
-     */
-    public void hide() {
-        mShowing = false;
-        this.setVisibility(GONE);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 20d19ec..bce172b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -115,7 +115,6 @@
     /** How long to wait, in milliseconds, before hiding the flyout. */
     @VisibleForTesting
     static final int FLYOUT_HIDE_AFTER = 5000;
-    private BubbleController.BubbleScreenshotListener mBubbleScreenshotListener;
 
     /**
      * Interface to synchronize {@link View} state and the screen.
@@ -169,7 +168,6 @@
     private ExpandedAnimationController mExpandedAnimationController;
 
     private FrameLayout mExpandedViewContainer;
-    @Nullable private BubbleMenuView mBubbleMenuView;
 
     private BubbleFlyoutView mFlyout;
     /** Runnable that fades out the flyout and then sets it to GONE. */
@@ -516,9 +514,6 @@
             mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
             mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
         });
-
-        mInflater.inflate(R.layout.bubble_menu_view, this);
-        mBubbleMenuView = findViewById(R.id.bubble_menu_container);
     }
 
     private void setUpOverflow() {
@@ -738,13 +733,6 @@
     }
 
     /**
-     * Sets the screenshot listener.
-     */
-    public void setBubbleScreenshotListener(BubbleController.BubbleScreenshotListener listener) {
-        mBubbleScreenshotListener = listener;
-    }
-
-    /**
      * Whether the stack of bubbles is expanded or not.
      */
     public boolean isExpanded() {
@@ -942,12 +930,6 @@
     public View getTargetView(MotionEvent event) {
         float x = event.getRawX();
         float y = event.getRawY();
-        if (mBubbleMenuView.isShowing()) {
-            if (isIntersecting(mBubbleMenuView.getMenuView(), x, y)) {
-                return mBubbleMenuView;
-            }
-            return null;
-        }
         if (mIsExpanded) {
             if (isIntersecting(mBubbleContainer, x, y)) {
                 if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
@@ -1168,7 +1150,6 @@
             }
             return;
         }
-        hideBubbleMenu();
         mStackAnimationController.cancelStackPositionAnimations();
         mBubbleContainer.setActiveController(mStackAnimationController);
         hideFlyoutImmediate();
@@ -1570,10 +1551,6 @@
     @Override
     public void getBoundsOnScreen(Rect outRect) {
         // If the bubble menu is open, the entire screen should capture touch events.
-        if (mBubbleMenuView.isShowing()) {
-            outRect.set(0, 0, getWidth(), getHeight());
-            return;
-        }
         if (!mIsExpanded) {
             if (getBubbleCount() > 0) {
                 mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
@@ -1808,50 +1785,4 @@
         }
         return bubbles;
     }
-
-    /**
-     * Show the bubble menu, positioned relative to the stack.
-     */
-    public void showBubbleMenu() {
-        PointF currentPos = mStackAnimationController.getStackPosition();
-        mBubbleMenuView.setVisibility(View.INVISIBLE);
-        post(() -> {
-            float yPos = currentPos.y;
-            float xPos = currentPos.x;
-            if (mStackAnimationController.isStackOnLeftSide()) {
-                xPos += mBubbleSize;
-            } else {
-                xPos -= mBubbleMenuView.getMenuView().getWidth();
-            }
-
-            mBubbleMenuView.show(xPos, yPos);
-        });
-    }
-
-    /**
-     * Hide the bubble menu.
-     */
-    public void hideBubbleMenu() {
-        mBubbleMenuView.hide();
-    }
-
-    /**
-     * Determines whether the bubble menu is currently showing.
-     */
-    public boolean isShowingBubbleMenu() {
-        return mBubbleMenuView.isShowing();
-    }
-
-    /**
-     * Take a screenshot and send it to the specified bubble.
-     */
-    public void sendScreenshotToBubble(Bubble bubble) {
-        hideBubbleMenu();
-        // delay allows the bubble menu to disappear before the screenshot
-        // done here because we already have a Handler to delay with.
-        // TODO: Hide bubble + menu UI from screenshots entirely instead of just delaying.
-        postDelayed(() -> {
-            mBubbleScreenshotListener.onBubbleScreenshot(bubble);
-        }, BubbleMenuView.SCREENSHOT_DELAY);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 5a9d44b..645696d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -58,14 +58,12 @@
     private final PointF mViewPositionOnTouchDown = new PointF();
     private final BubbleStackView mStack;
     private final BubbleData mBubbleData;
-    private final Context mContext;
 
     private BubbleController mController = Dependency.get(BubbleController.class);
 
     private boolean mMovedEnough;
     private int mTouchSlopSquared;
     private VelocityTracker mVelocityTracker;
-    private Runnable mShowBubbleMenuRunnable;
 
     /** View that was initially touched, when we received the first ACTION_DOWN event. */
     private View mTouchedView;
@@ -78,7 +76,6 @@
         mTouchSlopSquared = touchSlop * touchSlop;
         mBubbleData = bubbleData;
         mStack = stackView;
-        mContext = context;
     }
 
     @Override
@@ -95,18 +92,10 @@
         // anything, collapse the stack.
         if (action == MotionEvent.ACTION_OUTSIDE || mTouchedView == null) {
             mBubbleData.setExpanded(false);
-            mStack.hideBubbleMenu();
             resetForNextGesture();
             return false;
         }
 
-        if (mTouchedView instanceof BubbleMenuView) {
-            mStack.hideBubbleMenu();
-            resetForNextGesture();
-            mStack.sendScreenshotToBubble(mBubbleData.getSelectedBubble());
-            return false;
-        }
-
         if (!(mTouchedView instanceof BadgedImageView)
                 && !(mTouchedView instanceof BubbleStackView)
                 && !(mTouchedView instanceof BubbleFlyoutView)) {
@@ -116,7 +105,6 @@
             }
             // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
             // of expanded view).
-            mStack.hideBubbleMenu();
             resetForNextGesture();
             return false;
         }
@@ -139,12 +127,6 @@
                 if (isStack) {
                     mViewPositionOnTouchDown.set(mStack.getStackPosition());
                     mStack.onDragStart();
-                    if (!mStack.isShowingBubbleMenu() && !mStack.isExpanded()
-                            && BubbleExperimentConfig.allowBubbleScreenshotMenu(mContext)) {
-                        mShowBubbleMenuRunnable = mStack::showBubbleMenu;
-                        mStack.postDelayed(mShowBubbleMenuRunnable,
-                                ViewConfiguration.getLongPressTimeout());
-                    }
                 } else if (isFlyout) {
                     mStack.onFlyoutDragStart();
                 } else {
@@ -155,10 +137,6 @@
 
                 break;
             case MotionEvent.ACTION_MOVE:
-                // block all further touch inputs once the menu is open
-                if (mStack.isShowingBubbleMenu()) {
-                    return true;
-                }
                 trackMovement(event);
                 final float deltaX = rawX - mTouchDown.x;
                 final float deltaY = rawY - mTouchDown.y;
@@ -168,7 +146,6 @@
                 }
 
                 if (mMovedEnough) {
-                    mStack.removeCallbacks(mShowBubbleMenuRunnable);
                     if (isStack) {
                         mStack.onDragged(viewX, viewY);
                     } else if (isFlyout) {
@@ -199,12 +176,6 @@
                 break;
 
             case MotionEvent.ACTION_UP:
-                if (mStack.isShowingBubbleMenu()) {
-                    resetForNextGesture();
-                    return true;
-                } else {
-                    mStack.removeCallbacks(mShowBubbleMenuRunnable);
-                }
                 trackMovement(event);
                 mVelocityTracker.computeCurrentVelocity(/* maxVelocity */ 1000);
                 final float velX = mVelocityTracker.getXVelocity();
@@ -227,9 +198,14 @@
                                 if (isStack) {
                                     mController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
                                 } else {
-                                    mController.removeBubble(
-                                            individualBubbleKey,
-                                            BubbleController.DISMISS_USER_GESTURE);
+                                    final Bubble bubble =
+                                            mBubbleData.getBubbleWithKey(individualBubbleKey);
+                                    // bubble can be null if the user is in the middle of
+                                    // dismissing the bubble, but the app also sent a cancel
+                                    if (bubble != null) {
+                                        mController.removeBubble(bubble.getEntry(),
+                                                BubbleController.DISMISS_USER_GESTURE);
+                                    }
                                 }
                             });
                 } else if (isFlyout) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 88b19b5..78e0e8b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -39,7 +39,7 @@
 
 const val MIN_LEVEL = 0
 const val MAX_LEVEL = 10000
-private const val UPDATE_DELAY_IN_MILLIS = 2000L
+private const val UPDATE_DELAY_IN_MILLIS = 3000L
 
 class ControlViewHolder(
     val layout: ViewGroup,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index ed521e3..f029dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -22,6 +22,7 @@
 import android.content.Context
 import android.content.Intent
 import android.content.ServiceConnection
+import android.graphics.drawable.Drawable
 import android.os.IBinder
 import android.service.controls.Control
 import android.service.controls.TokenProvider
@@ -29,19 +30,24 @@
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.Space
-import android.widget.TextView
 
+import com.android.settingslib.widget.CandidateInfo
 import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlInfo
+import com.android.systemui.controls.management.ControlsListingController
 import com.android.systemui.controls.management.ControlsProviderSelectorActivity
+import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.R
 import com.android.systemui.util.concurrency.DelayableExecutor
 
 import dagger.Lazy
 
+import java.text.Collator
+
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -110,7 +116,9 @@
 class ControlsUiControllerImpl @Inject constructor (
     val controlsController: Lazy<ControlsController>,
     val context: Context,
-    @Main val uiExecutor: DelayableExecutor
+    @Main val uiExecutor: DelayableExecutor,
+    @Background val bgExecutor: DelayableExecutor,
+    val controlsListingController: Lazy<ControlsListingController>
 ) : ControlsUiController {
 
     private lateinit var controlInfos: List<ControlInfo>
@@ -121,6 +129,22 @@
     override val available: Boolean
         get() = controlsController.get().available
 
+    private val listingCallback = object : ControlsListingController.ControlsListingCallback {
+        override fun onServicesUpdated(candidates: List<CandidateInfo>) {
+            bgExecutor.execute {
+                val collator = Collator.getInstance(context.getResources()
+                        .getConfiguration().locale)
+                val localeComparator = compareBy<CandidateInfo, CharSequence>(collator) {
+                    it.loadLabel()
+                }
+
+                val mList = candidates.toMutableList()
+                mList.sortWith(localeComparator)
+                loadInitialSetupViewIcons(mList.map { it.loadLabel() to it.loadIcon() })
+            }
+        }
+    }
+
     override fun show(parent: ViewGroup) {
         Log.d(TAG, "show()")
 
@@ -153,8 +177,26 @@
         val inflater = LayoutInflater.from(context)
         inflater.inflate(R.layout.controls_no_favorites, parent, true)
 
-        val textView = parent.requireViewById(R.id.controls_title) as TextView
-        textView.setOnClickListener(launchSelectorActivityListener(context))
+        val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
+        viewGroup.setOnClickListener(launchSelectorActivityListener(context))
+
+        controlsListingController.get().addCallback(listingCallback)
+    }
+
+    private fun loadInitialSetupViewIcons(icons: List<Pair<CharSequence, Drawable>>) {
+        uiExecutor.execute {
+            val viewGroup = parent.requireViewById(R.id.controls_icon_row) as ViewGroup
+            viewGroup.removeAllViews()
+
+            val inflater = LayoutInflater.from(context)
+            icons.forEach {
+                val imageView = inflater.inflate(R.layout.controls_icon, viewGroup, false)
+                        as ImageView
+                imageView.setContentDescription(it.first)
+                imageView.setImageDrawable(it.second)
+                viewGroup.addView(imageView)
+            }
+        }
     }
 
     private fun launchSelectorActivityListener(context: Context): (View) -> Unit {
@@ -206,6 +248,7 @@
         parent.removeAllViews()
         controlsById.clear()
         controlViewsById.clear()
+        controlsListingController.get().removeCallback(listingCallback)
     }
 
     override fun onRefreshState(componentName: ComponentName, controls: List<Control>) {
@@ -231,9 +274,9 @@
         }
     }
 
-    private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup {
-        val row = inflater.inflate(R.layout.controls_row, parent, false) as ViewGroup
-        parent.addView(row)
+    private fun createRow(inflater: LayoutInflater, listView: ViewGroup): ViewGroup {
+        val row = inflater.inflate(R.layout.controls_row, listView, false) as ViewGroup
+        listView.addView(row)
         return row
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 082b065..b3fc027 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1725,7 +1725,8 @@
             if (!(mBackgroundDrawable instanceof ScrimDrawable)) {
                 return;
             }
-            ((ScrimDrawable) mBackgroundDrawable).setColor(Color.BLACK, animate);
+            ((ScrimDrawable) mBackgroundDrawable).setColor(colors.supportsDarkText() ? Color.WHITE
+                    : Color.BLACK, animate);
             View decorView = getWindow().getDecorView();
             if (colors.supportsDarkText()) {
                 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index c9c38d3..146f36b 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -36,8 +36,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -49,8 +47,7 @@
 
 import dagger.Lazy;
 
-public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks,
-        PluginListener<GlobalActionsPanelPlugin> {
+public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
 
     private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
 
@@ -60,12 +57,9 @@
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
     private final BlurUtils mBlurUtils;
-    private GlobalActionsPanelPlugin mPlugin;
     private final CommandQueue mCommandQueue;
     private GlobalActionsDialog mGlobalActionsDialog;
     private boolean mDisabled;
-    private final PluginManager mPluginManager;
-    private final String mPluginPackageName;
 
     @Inject
     public GlobalActionsImpl(Context context, CommandQueue commandQueue,
@@ -74,7 +68,6 @@
         mGlobalActionsDialogLazy = globalActionsDialogLazy;
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mPluginManager = Dependency.get(PluginManager.class);
         mCommandQueue = commandQueue;
         mBlurUtils = blurUtils;
         mCommandQueue.addCallback(this);
@@ -82,17 +75,11 @@
                 .newExtension(GlobalActionsPanelPlugin.class)
                 .withPlugin(GlobalActionsPanelPlugin.class)
                 .build();
-        mPluginPackageName = mContext.getString(
-                com.android.systemui.R.string.config_controlsPluginPackageName);
-        mPluginManager.addPluginListener(
-                GlobalActionsPanelPlugin.ACTION, this, GlobalActionsPanelPlugin.class, true);
     }
 
     @Override
     public void destroy() {
         mCommandQueue.removeCallback(this);
-        mPluginManager.removePluginListener(this);
-        if (mPlugin != null) mPlugin.onDestroy();
         if (mGlobalActionsDialog != null) {
             mGlobalActionsDialog.destroy();
             mGlobalActionsDialog = null;
@@ -105,7 +92,7 @@
         mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
         mGlobalActionsDialog.showDialog(mKeyguardStateController.isShowing(),
                 mDeviceProvisionedController.isDeviceProvisioned(),
-                mPlugin != null ? mPlugin : mPanelExtension.get());
+                mPanelExtension.get());
         Dependency.get(KeyguardUpdateMonitor.class).requestFaceAuth();
     }
 
@@ -205,16 +192,4 @@
             mGlobalActionsDialog.dismissDialog();
         }
     }
-
-    @Override
-    public void onPluginConnected(GlobalActionsPanelPlugin plugin, Context pluginContext) {
-        if (pluginContext.getPackageName().equals(mPluginPackageName)) {
-            mPlugin = plugin;
-        }
-    }
-
-    @Override
-    public void onPluginDisconnected(GlobalActionsPanelPlugin plugin) {
-        mPlugin = null;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 14eec59..9da99c4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -151,7 +151,7 @@
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
 
-    private static final boolean DEBUG = KeyguardConstants.DEBUG;
+    private static final boolean DEBUG = true;
     private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
 
     private final static String TAG = "KeyguardViewMediator";
diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java
deleted file mode 100644
index 7bc1abf..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/Event.java
+++ /dev/null
@@ -1,84 +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.log;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
- * Every event has a time stamp, log level and message.
- * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
- */
-public class Event {
-    public static final int UNINITIALIZED = -1;
-
-    @IntDef({ERROR, WARN, INFO, DEBUG, VERBOSE})
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Level {}
-    public static final int VERBOSE = 2;
-    public static final int DEBUG = 3;
-    public static final int INFO = 4;
-    public static final int WARN = 5;
-    public static final int ERROR = 6;
-    public static final @Level int DEFAULT_LOG_LEVEL = DEBUG;
-
-    private long mTimestamp;
-    private @Level int mLogLevel = DEFAULT_LOG_LEVEL;
-    private String mMessage = "";
-
-    /**
-     * initialize an event with a message
-     */
-    public Event init(String message) {
-        init(DEFAULT_LOG_LEVEL, message);
-        return this;
-    }
-
-    /**
-     * initialize an event with a logLevel and message
-     */
-    public Event init(@Level int logLevel, String message) {
-        mTimestamp = System.currentTimeMillis();
-        mLogLevel = logLevel;
-        mMessage = message;
-        return this;
-    }
-
-    public String getMessage() {
-        return mMessage;
-    }
-
-    public long getTimestamp() {
-        return mTimestamp;
-    }
-
-    public @Level int getLogLevel() {
-        return mLogLevel;
-    }
-
-    /**
-     * Recycle this event
-     */
-    void recycle() {
-        mTimestamp = -1;
-        mLogLevel = DEFAULT_LOG_LEVEL;
-        mMessage = "";
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
deleted file mode 100644
index 470f2b0..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java
+++ /dev/null
@@ -1,123 +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.log;
-
-/**
- * Stores information about an event that occurred in SystemUI to be used for debugging and triage.
- * Every rich event has a time stamp, event type, and log level, with the option to provide the
- * reason this event was triggered.
- * Events are stored in {@link SysuiLog} and can be printed in a dumpsys.
- */
-public abstract class RichEvent extends Event {
-    private int mType;
-
-    /**
-     * Initializes a rich event that includes an event type that matches with an index in the array
-     * getEventLabels().
-     */
-    public RichEvent init(@Event.Level int logLevel, int type, String reason) {
-        final int numEvents = getEventLabels().length;
-        if (type < 0 || type >= numEvents) {
-            throw new IllegalArgumentException("Unsupported event type. Events only supported"
-                    + " from 0 to " + (numEvents - 1) + ", but given type=" + type);
-        }
-        mType = type;
-        super.init(logLevel, getEventLabels()[mType] + " " + reason);
-        return this;
-    }
-
-    /**
-     * Returns an array of the event labels.  The index represents the event type and the
-     * corresponding String stored at that index is the user-readable representation of that event.
-     * @return array of user readable events, where the index represents its event type constant
-     */
-    public abstract String[] getEventLabels();
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        mType = -1;
-    }
-
-    public int getType() {
-        return mType;
-    }
-
-    /**
-     * Builder to build a RichEvent.
-     * @param <B> Log specific builder that is extending this builder
-     * @param <E> Type of event we'll be building
-     */
-    public abstract static class Builder<B extends Builder<B, E>, E extends RichEvent> {
-        public static final int UNINITIALIZED = -1;
-
-        public final SysuiLog mLog;
-        private B mBuilder = getBuilder();
-        protected int mType;
-        protected String mReason;
-        protected @Level int mLogLevel;
-
-        public Builder(SysuiLog sysuiLog) {
-            mLog = sysuiLog;
-            reset();
-        }
-
-        /**
-         * Reset this builder's parameters so it can be reused to build another RichEvent.
-         */
-        public void reset() {
-            mType = UNINITIALIZED;
-            mReason = null;
-            mLogLevel = VERBOSE;
-        }
-
-        /**
-         * Get the log-specific builder.
-         */
-        public abstract B getBuilder();
-
-        /**
-         * Build the log-specific event given an event to populate.
-         */
-        public abstract E build(E e);
-
-        /**
-         * Optional - set the log level. Defaults to DEBUG.
-         */
-        public B setLogLevel(@Level int logLevel) {
-            mLogLevel = logLevel;
-            return mBuilder;
-        }
-
-        /**
-         * Required - set the event type.  These events must correspond with the events from
-         * getEventLabels().
-         */
-        public B setType(int type) {
-            mType = type;
-            return mBuilder;
-        }
-
-        /**
-         * Optional - set the reason why this event was triggered.
-         */
-        public B setReason(String reason) {
-            mReason = reason;
-            return mBuilder;
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
deleted file mode 100644
index 9ee3e67..0000000
--- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java
+++ /dev/null
@@ -1,180 +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.log;
-
-import android.os.Build;
-import android.os.SystemProperties;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.DumpController;
-import com.android.systemui.Dumpable;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.text.SimpleDateFormat;
-import java.util.ArrayDeque;
-import java.util.Locale;
-
-/**
- * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be
- * printed by the DumpController. This is an alternative to printing directly
- * to avoid logs being deleted by chatty. The number of logs retained is varied based on
- * whether the build is {@link Build.IS_DEBUGGABLE}.
- *
- * To manually view the logs via adb:
- *      adb shell dumpsys activity service com.android.systemui/.SystemUIService \
- *      dependency DumpController <SysuiLogId>
- *
- * Logs can be disabled by setting the following SystemProperty and then restarting the device:
- *      adb shell setprop persist.sysui.log.enabled.<id> true/false && adb reboot
- *
- * @param <E> Type of event we'll be logging
- */
-public class SysuiLog<E extends Event> implements Dumpable {
-    public static final SimpleDateFormat DATE_FORMAT =
-            new SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US);
-
-    protected final Object mDataLock = new Object();
-    private final String mId;
-    private final int mMaxLogs;
-    protected boolean mEnabled;
-    protected boolean mLogToLogcatEnabled;
-
-    @VisibleForTesting protected ArrayDeque<E> mTimeline;
-
-    /**
-     * Creates a SysuiLog
-     * @param dumpController where to register this logger's dumpsys
-     * @param id user-readable tag for this logger
-     * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true
-     * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false
-     */
-    public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) {
-        this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs,
-                SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED),
-                SystemProperties.getBoolean(SYSPROP_LOGCAT_ENABLED_PREFIX + id,
-                        DEFAULT_LOGCAT_ENABLED));
-    }
-
-    @VisibleForTesting
-    protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled,
-            boolean logcatEnabled) {
-        mId = id;
-        mMaxLogs = maxLogs;
-        mEnabled = enabled;
-        mLogToLogcatEnabled = logcatEnabled;
-        mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null;
-        dumpController.registerDumpable(mId, this);
-    }
-
-    /**
-     * Logs an event to the timeline which can be printed by the dumpsys.
-     * May also log to logcat if enabled.
-     * @return the last event that was discarded from the Timeline (can be recycled)
-     */
-    public E log(E event) {
-        if (!mEnabled) {
-            return null;
-        }
-
-        E recycledEvent = null;
-        synchronized (mDataLock) {
-            if (mTimeline.size() >= mMaxLogs) {
-                recycledEvent = mTimeline.removeFirst();
-            }
-
-            mTimeline.add(event);
-        }
-
-        if (mLogToLogcatEnabled) {
-            final String strEvent = eventToString(event);
-            switch (event.getLogLevel()) {
-                case Event.VERBOSE:
-                    Log.v(mId, strEvent);
-                    break;
-                case Event.DEBUG:
-                    Log.d(mId, strEvent);
-                    break;
-                case Event.ERROR:
-                    Log.e(mId, strEvent);
-                    break;
-                case Event.INFO:
-                    Log.i(mId, strEvent);
-                    break;
-                case Event.WARN:
-                    Log.w(mId, strEvent);
-                    break;
-            }
-        }
-
-        if (recycledEvent != null) {
-            recycledEvent.recycle();
-        }
-
-        return recycledEvent;
-    }
-
-    /**
-     * @return user-readable string of the given event with timestamp
-     */
-    private String eventToTimestampedString(Event event) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp()));
-        sb.append(" ");
-        sb.append(event.getMessage());
-        return sb.toString();
-    }
-
-    /**
-     * @return user-readable string of the given event without a timestamp
-     */
-    public String eventToString(Event event) {
-        return event.getMessage();
-    }
-
-    @GuardedBy("mDataLock")
-    private void dumpTimelineLocked(PrintWriter pw) {
-        pw.println("\tTimeline:");
-
-        for (Event event : mTimeline) {
-            pw.println("\t" + eventToTimestampedString(event));
-        }
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(mId + ":");
-
-        if (mEnabled) {
-            synchronized (mDataLock) {
-                dumpTimelineLocked(pw);
-            }
-        } else {
-            pw.print(" - Logging disabled.");
-        }
-    }
-
-    private static boolean sDebuggable = Build.IS_DEBUGGABLE;
-    private static final String SYSPROP_ENABLED_PREFIX = "persist.sysui.log.enabled.";
-    private static final String SYSPROP_LOGCAT_ENABLED_PREFIX = "persist.sysui.log.enabled.logcat.";
-    private static final boolean DEFAULT_ENABLED = sDebuggable;
-    private static final boolean DEFAULT_LOGCAT_ENABLED = false;
-    private static final int DEFAULT_MAX_DEBUG_LOGS = 100;
-    private static final int DEFAULT_MAX_LOGS = 50;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 411980b..ae61622 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -83,7 +83,7 @@
         mTile = new Tile();
         updateDefaultTileAndIcon();
         mServiceManager = host.getTileServices().getTileWrapper(this);
-        if (mServiceManager.isBooleanTile()) {
+        if (mServiceManager.isToggleableTile()) {
             // Replace states with BooleanState
             resetStates();
         }
@@ -252,7 +252,7 @@
 
     @Override
     public State newTileState() {
-        if (mServiceManager != null && mServiceManager.isBooleanTile()) {
+        if (mServiceManager != null && mServiceManager.isToggleableTile()) {
             return new BooleanState();
         }
         return new State();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index ad79cad..17b0251 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -141,16 +141,16 @@
     /**
      * Determines whether the associated TileService is a Boolean Tile.
      *
-     * @return true if {@link TileService#META_DATA_BOOLEAN_TILE} is set to {@code true} for this
+     * @return true if {@link TileService#META_DATA_TOGGLEABLE_TILE} is set to {@code true} for this
      *         tile
-     * @see TileService#META_DATA_BOOLEAN_TILE
+     * @see TileService#META_DATA_TOGGLEABLE_TILE
      */
-    public boolean isBooleanTile() {
+    public boolean isToggleableTile() {
         try {
             ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
                     PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
             return info.metaData != null
-                    && info.metaData.getBoolean(TileService.META_DATA_BOOLEAN_TILE, false);
+                    && info.metaData.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false);
         } catch (PackageManager.NameNotFoundException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 1902d65..cfa8fb6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -124,8 +124,8 @@
         return mStateManager.isActiveTile();
     }
 
-    public boolean isBooleanTile() {
-        return mStateManager.isBooleanTile();
+    public boolean isToggleableTile() {
+        return mStateManager.isToggleableTile();
     }
 
     public void setShowingDialog(boolean dialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 001e094..42f8010 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.UserManager;
 import android.service.quicksettings.Tile;
+import android.util.Log;
 import android.widget.Switch;
 
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -200,6 +201,14 @@
             mCallbackInfo.numConnectedDevices = numDevices;
             refreshState(mCallbackInfo);
         }
+
+        @Override
+        public void onHotspotAvailabilityChanged(boolean available) {
+            if (!available) {
+                Log.d(TAG, "Tile removed. Hotspot no longer available");
+                mHost.removeTile(getTileSpec());
+            }
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7ad07c2..7d3d406 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -20,7 +20,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.Resources;
 import android.graphics.Color;
 import android.hardware.biometrics.BiometricSourceType;
 import android.hardware.face.FaceManager;
@@ -46,6 +45,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.settingslib.Utils;
+import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -97,8 +97,6 @@
     private final LockPatternUtils mLockPatternUtils;
     private final DockManager mDockManager;
 
-    private final int mSlowThreshold;
-    private final int mFastThreshold;
     private final LockIcon mLockIcon;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
@@ -178,10 +176,6 @@
         mWakeLock = new SettableWakeLock(wakeLock, TAG);
         mLockPatternUtils = lockPatternUtils;
 
-        Resources res = context.getResources();
-        mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
-        mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
-
         mUserManager = context.getSystemService(UserManager.class);
         mBatteryInfo = iBatteryStats;
 
@@ -484,12 +478,12 @@
         int chargingId;
         if (mPowerPluggedInWired) {
             switch (mChargingSpeed) {
-                case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
+                case BatteryStatus.CHARGING_FAST:
                     chargingId = hasChargingTime
                             ? R.string.keyguard_indication_charging_time_fast
                             : R.string.keyguard_plugged_in_charging_fast;
                     break;
-                case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
+                case BatteryStatus.CHARGING_SLOWLY:
                     chargingId = hasChargingTime
                             ? R.string.keyguard_indication_charging_time_slowly
                             : R.string.keyguard_plugged_in_charging_slowly;
@@ -620,7 +614,7 @@
         public static final int HIDE_DELAY_MS = 5000;
 
         @Override
-        public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
+        public void onRefreshBatteryInfo(BatteryStatus status) {
             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
             boolean wasPluggedIn = mPowerPluggedIn;
@@ -628,7 +622,7 @@
             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
             mPowerCharged = status.isCharged();
             mChargingWattage = status.maxChargingWattage;
-            mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
+            mChargingSpeed = status.getChargingSpeed(mContext);
             mBatteryLevel = status.level;
             try {
                 mChargingTimeRemaining = mPowerPluggedIn
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 61915ad..916da6e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -47,8 +47,6 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -129,6 +127,8 @@
     private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications =
             new ArrayMap<>();
 
+    private final NotificationEntryManagerLogger mLogger;
+
     // Lazily retrieved dependencies
     private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
     private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
@@ -143,7 +143,6 @@
 
     private NotificationPresenter mPresenter;
     private RankingMap mLatestRankingMap;
-    private NotifLog mNotifLog;
 
     @VisibleForTesting
     final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders
@@ -184,7 +183,7 @@
 
     @Inject
     public NotificationEntryManager(
-            NotifLog notifLog,
+            NotificationEntryManagerLogger logger,
             NotificationGroupManager groupManager,
             NotificationRankingManager rankingManager,
             KeyguardEnvironment keyguardEnvironment,
@@ -193,7 +192,7 @@
             Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
             LeakDetector leakDetector,
             ForegroundServiceDismissalFeatureController fgsFeatureController) {
-        mNotifLog = notifLog;
+        mLogger = logger;
         mGroupManager = groupManager;
         mRankingManager = rankingManager;
         mKeyguardEnvironment = keyguardEnvironment;
@@ -291,13 +290,12 @@
             NotificationEntry entry = mPendingNotifications.get(key);
             entry.abortTask();
             mPendingNotifications.remove(key);
-            mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry, "PendingNotification aborted"
-                    + " reason=" + reason);
+            mLogger.logInflationAborted(key, "pending", reason);
         }
         NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
         if (addedEntry != null) {
             addedEntry.abortTask();
-            mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getKey() + " " + reason);
+            mLogger.logInflationAborted(key, "active", reason);
         }
     }
 
@@ -328,9 +326,9 @@
         // the list, otherwise we might get leaks.
         if (!entry.isRowRemoved()) {
             boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null;
+            mLogger.logNotifInflated(entry.getKey(), isNew);
             if (isNew) {
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
-                    mNotifLog.log(NotifEvent.INFLATED, entry);
                     listener.onEntryInflated(entry);
                 }
                 addActiveNotification(entry);
@@ -340,7 +338,6 @@
                 }
             } else {
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
-                    mNotifLog.log(NotifEvent.INFLATED, entry);
                     listener.onEntryReinflated(entry);
                 }
             }
@@ -422,7 +419,7 @@
         for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) {
             if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
                 // Remove intercepted; log and skip
-                mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED);
+                mLogger.logRemovalIntercepted(key);
                 return;
             }
         }
@@ -437,10 +434,7 @@
                     if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) {
                         extendLifetime(pendingEntry, extender);
                         lifetimeExtended = true;
-                        mNotifLog.log(
-                                NotifEvent.LIFETIME_EXTENDED,
-                                pendingEntry.getSbn(),
-                                "pendingEntry extendedBy=" + extender.toString());
+                        mLogger.logLifetimeExtended(key, extender.getClass().getName(), "pending");
                     }
                 }
             }
@@ -460,10 +454,7 @@
                         mLatestRankingMap = ranking;
                         extendLifetime(entry, extender);
                         lifetimeExtended = true;
-                        mNotifLog.log(
-                                NotifEvent.LIFETIME_EXTENDED,
-                                entry.getSbn(),
-                                "entry extendedBy=" + extender.toString());
+                        mLogger.logLifetimeExtended(key, extender.getClass().getName(), "active");
                         break;
                     }
                 }
@@ -486,8 +477,7 @@
                 mLeakDetector.trackGarbage(entry);
                 removedByUser |= entryDismissed;
 
-                mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(),
-                        "removedByUser=" + removedByUser);
+                mLogger.logNotifRemoved(entry.getKey(), removedByUser);
                 for (NotificationEntryListener listener : mNotificationEntryListeners) {
                     listener.onEntryRemoved(entry, visibility, removedByUser);
                 }
@@ -576,7 +566,7 @@
 
         abortExistingInflation(key, "addNotification");
         mPendingNotifications.put(key, entry);
-        mNotifLog.log(NotifEvent.NOTIF_ADDED, entry);
+        mLogger.logNotifAdded(entry.getKey());
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPendingEntryAdded(entry);
         }
@@ -613,7 +603,7 @@
         entry.setSbn(notification);
         mGroupManager.onEntryUpdated(entry, oldSbn);
 
-        mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry);
+        mLogger.logNotifUpdated(entry.getKey());
         for (NotificationEntryListener listener : mNotificationEntryListeners) {
             listener.onPreEntryUpdated(entry);
         }
@@ -808,7 +798,7 @@
     //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking
     /**
      * @param rankingMap the {@link RankingMap} to apply to the current notification list
-     * @param reason the reason for calling this method, for {@link NotifLog}
+     * @param reason the reason for calling this method, which will be logged
      */
     public void updateRanking(RankingMap rankingMap, String reason) {
         updateRankingAndSort(rankingMap, reason);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
new file mode 100644
index 0000000..4382ab5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+/** Logger for [NotificationEntryManager]. */
+class NotificationEntryManagerLogger @Inject constructor(
+    @NotificationLog private val buffer: LogBuffer
+) {
+    fun logNotifAdded(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "NOTIF ADDED $str1"
+        })
+    }
+
+    fun logNotifUpdated(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "NOTIF UPDATED $str1"
+        })
+    }
+
+    fun logInflationAborted(key: String, status: String, reason: String) {
+        buffer.log(TAG, DEBUG, {
+            str1 = key
+            str2 = status
+            str3 = reason
+        }, {
+            "NOTIF INFLATION ABORTED $str1 notifStatus=$str2 reason=$str3"
+        })
+    }
+
+    fun logNotifInflated(key: String, isNew: Boolean) {
+        buffer.log(TAG, DEBUG, {
+            str1 = key
+            bool1 = isNew
+        }, {
+            "NOTIF INFLATED $str1 isNew=$bool1}"
+        })
+    }
+
+    fun logRemovalIntercepted(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "NOTIF REMOVE INTERCEPTED for $str1"
+        })
+    }
+
+    fun logLifetimeExtended(key: String, extenderName: String, status: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+            str2 = extenderName
+            str3 = status
+        }, {
+            "NOTIF LIFETIME EXTENDED $str1 extender=$str2 status=$str3"
+        })
+    }
+
+    fun logNotifRemoved(key: String, removedByUser: Boolean) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+            bool1 = removedByUser
+        }, {
+            "NOTIF REMOVED $str1 removedByUser=$bool1"
+        })
+    }
+
+    fun logFilterAndSort(reason: String) {
+        buffer.log(TAG, INFO, {
+            str1 = reason
+        }, {
+            "FILTER AND SORT reason=$str1"
+        })
+    }
+}
+
+private const val TAG = "NotificationEntryMgr"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index 7fe229c..3fa1954 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -123,6 +123,16 @@
                         .append(" ");
             }
 
+            if (!notifEntry.mDismissInterceptors.isEmpty()) {
+                String[] interceptorsNames = new String[notifEntry.mDismissInterceptors.size()];
+                for (int i = 0; i < interceptorsNames.length; i++) {
+                    interceptorsNames[i] = notifEntry.mDismissInterceptors.get(i).getName();
+                }
+                rksb.append("dismissInterceptors=")
+                        .append(Arrays.toString(interceptorsNames))
+                        .append(" ");
+            }
+
             if (notifEntry.mExcludingFilter != null) {
                 rksb.append("filter=")
                         .append(notifEntry.mExcludingFilter)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 3b2fe94..38d8d97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -63,6 +63,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.util.Assert;
 
@@ -116,6 +117,7 @@
     @Nullable private CollectionReadyForBuildListener mBuildListener;
     private final List<NotifCollectionListener> mNotifCollectionListeners = new ArrayList<>();
     private final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
+    private final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
 
     private boolean mAttached = false;
     private boolean mAmDispatchingToOtherCode;
@@ -176,10 +178,21 @@
         extender.setCallback(this::onEndLifetimeExtension);
     }
 
+    /** @see NotifPipeline#addNotificationDismissInterceptor(NotifDismissInterceptor) */
+    void addNotificationDismissInterceptor(NotifDismissInterceptor interceptor) {
+        Assert.isMainThread();
+        checkForReentrantCall();
+        if (mDismissInterceptors.contains(interceptor)) {
+            throw new IllegalArgumentException("Interceptor " + interceptor + " already added.");
+        }
+        mDismissInterceptors.add(interceptor);
+        interceptor.setCallback(this::onEndDismissInterception);
+    }
+
     /**
      * Dismiss a notification on behalf of the user.
      */
-    void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) {
+    public void dismissNotification(NotificationEntry entry, @NonNull DismissedByUserStats stats) {
         Assert.isMainThread();
         requireNonNull(stats);
         checkForReentrantCall();
@@ -192,6 +205,12 @@
             return;
         }
 
+        updateDismissInterceptors(entry);
+        if (isDismissIntercepted(entry)) {
+            mLogger.logNotifDismissedIntercepted(entry.getKey());
+            return;
+        }
+
         // Optimistically mark the notification as dismissed -- we'll wait for the signal from
         // system server before removing it from our notification set.
         entry.setDismissState(DISMISSED);
@@ -236,7 +255,6 @@
         for (NotificationEntry canceledEntry : canceledEntries) {
             tryRemoveNotification(canceledEntry);
         }
-
         rebuildList();
     }
 
@@ -307,11 +325,11 @@
             // Update to an existing entry
             mLogger.logNotifUpdated(sbn.getKey());
 
+            // Notification is updated so it is essentially re-added and thus alive again, so we
+            // can reset its state.
             cancelLocalDismissal(entry);
-
-            // Notification is updated so it is essentially re-added and thus alive again. Don't
-            // need to keep its lifetime extended.
             cancelLifetimeExtension(entry);
+            cancelDismissInterception(entry);
             entry.mCancellationReason = REASON_NOT_CANCELED;
 
             entry.setSbn(sbn);
@@ -348,6 +366,7 @@
 
         if (!isLifetimeExtended(entry)) {
             mNotificationSet.remove(entry.getKey());
+            cancelDismissInterception(entry);
             dispatchOnEntryRemoved(entry, entry.mCancellationReason);
             dispatchOnEntryCleanUp(entry);
             return true;
@@ -436,6 +455,17 @@
         mAmDispatchingToOtherCode = false;
     }
 
+    private void updateDismissInterceptors(@NonNull NotificationEntry entry) {
+        entry.mDismissInterceptors.clear();
+        mAmDispatchingToOtherCode = true;
+        for (NotifDismissInterceptor interceptor : mDismissInterceptors) {
+            if (interceptor.shouldInterceptDismissal(entry)) {
+                entry.mDismissInterceptors.add(interceptor);
+            }
+        }
+        mAmDispatchingToOtherCode = false;
+    }
+
     private void cancelLocalDismissal(NotificationEntry entry) {
         if (isDismissedByUser(entry)) {
             entry.setDismissState(NOT_DISMISSED);
@@ -450,6 +480,42 @@
         }
     }
 
+    private void onEndDismissInterception(
+            NotifDismissInterceptor interceptor,
+            NotificationEntry entry,
+            @NonNull DismissedByUserStats stats) {
+        Assert.isMainThread();
+        if (!mAttached) {
+            return;
+        }
+        checkForReentrantCall();
+
+        if (!entry.mDismissInterceptors.remove(interceptor)) {
+            throw new IllegalStateException(
+                    String.format(
+                            "Cannot end dismiss interceptor for interceptor \"%s\" (%s)",
+                            interceptor.getName(),
+                            interceptor));
+        }
+
+        if (!isDismissIntercepted(entry)) {
+            dismissNotification(entry, stats);
+        }
+    }
+
+    private void cancelDismissInterception(NotificationEntry entry) {
+        mAmDispatchingToOtherCode = true;
+        for (NotifDismissInterceptor interceptor : entry.mDismissInterceptors) {
+            interceptor.cancelDismissInterception(entry);
+        }
+        mAmDispatchingToOtherCode = false;
+        entry.mDismissInterceptors.clear();
+    }
+
+    private boolean isDismissIntercepted(NotificationEntry entry) {
+        return entry.mDismissInterceptors.size() > 0;
+    }
+
     private void checkForReentrantCall() {
         if (mAmDispatchingToOtherCode) {
             throw new IllegalStateException("Reentrant call detected");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
index 5767ad9..d4d2369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.java
@@ -25,6 +25,7 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 
 import java.util.Collection;
@@ -97,13 +98,21 @@
 
     /**
      * Registers a lifetime extender. Lifetime extenders can cause notifications that have been
-     * dismissed or retracted to be temporarily retained in the collection.
+     * dismissed or retracted by system server to be temporarily retained in the collection.
      */
     public void addNotificationLifetimeExtender(NotifLifetimeExtender extender) {
         mNotifCollection.addNotificationLifetimeExtender(extender);
     }
 
     /**
+     * Registers a dismiss interceptor. Dismiss interceptors can cause notifications that have been
+     * dismissed by the user to be retained (won't send a dismissal to system server).
+     */
+    public void addNotificationDismissInterceptor(NotifDismissInterceptor interceptor) {
+        mNotifCollection.addNotificationDismissInterceptor(interceptor);
+    }
+
+    /**
      * Registers a filter with the pipeline before grouping, promoting and sorting occurs. Filters
      * are called on each notification in the order that they were registered. If any filter
      * returns true, the notification is removed from the pipeline (and no other filters are
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 5dbf47e..41c1b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -66,6 +66,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -104,6 +105,9 @@
     /** List of lifetime extenders that are extending the lifetime of this notification. */
     final List<NotifLifetimeExtender> mLifetimeExtenders = new ArrayList<>();
 
+    /** List of dismiss interceptors that are intercepting the dismissal of this notification. */
+    final List<NotifDismissInterceptor> mDismissInterceptors = new ArrayList<>();
+
     /** If this notification was filtered out, then the filter that did the filtering. */
     @Nullable NotifFilter mExcludingFilter;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 1eeeab3..2981252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -22,11 +22,10 @@
 import android.service.notification.NotificationListenerService.RankingMap
 import android.service.notification.StatusBarNotification
 import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
 import com.android.systemui.statusbar.notification.NotificationFilter
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
-import com.android.systemui.statusbar.notification.logging.NotifEvent
-import com.android.systemui.statusbar.notification.logging.NotifLog
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
@@ -53,7 +52,7 @@
     private val groupManager: NotificationGroupManager,
     private val headsUpManager: HeadsUpManager,
     private val notifFilter: NotificationFilter,
-    private val notifLog: NotifLog,
+    private val logger: NotificationEntryManagerLogger,
     sectionsFeatureManager: NotificationSectionsFeatureManager,
     private val peopleNotificationIdentifier: PeopleNotificationIdentifier,
     private val highPriorityProvider: HighPriorityProvider
@@ -134,7 +133,7 @@
         entries: Sequence<NotificationEntry>,
         reason: String
     ): Sequence<NotificationEntry> {
-        notifLog.log(NotifEvent.FILTER_AND_SORT, reason)
+        logger.logFilterAndSort(reason)
 
         return entries.filter { !notifFilter.shouldFilterOut(it) }
                 .sortedWith(rankingComparator)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
new file mode 100644
index 0000000..116c70c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_UNKNOWN;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Coordinates hiding, intercepting (the dismissal), and deletion of bubbled notifications.
+ *
+ * The typical "start state" for a bubbled notification is when a bubble-able notification is
+ * posted. It is visible as a bubble AND as a notification in the shade. From here, we can get
+ * into a few hidden-from-shade states described below:
+ *
+ * Start State -> Hidden from shade
+ * User expands the bubble so we hide its notification from the shade.
+ * OR
+ * User dismisses a group summary with a bubbled child. All bubbled children are now hidden from
+ * the shade. And the group summary's dismissal is intercepted + hidden from the shade (see below).
+ *
+ * Start State -> Dismissal intercepted + hidden from shade
+ * User dismisses the notification from the shade. We now hide the notification from the shade
+ * and intercept its dismissal (the removal signal is never sent to system server). We
+ * keep the notification alive in system server so that {@link BubbleController} can still
+ * respond to app-cancellations (ie: remove the bubble if the app cancels the notification).
+ *
+ */
+@Singleton
+public class BubbleCoordinator implements Coordinator {
+    private static final String TAG = "BubbleCoordinator";
+
+    private final BubbleController mBubbleController;
+    private final NotifCollection mNotifCollection;
+    private final Set<String> mInterceptedDismissalEntries = new HashSet<>();
+    private NotifPipeline mNotifPipeline;
+    private NotifDismissInterceptor.OnEndDismissInterception mOnEndDismissInterception;
+
+    @Inject
+    public BubbleCoordinator(
+            BubbleController bubbleController,
+            NotifCollection notifCollection) {
+        mBubbleController = bubbleController;
+        mNotifCollection = notifCollection;
+    }
+
+    @Override
+    public void attach(NotifPipeline pipeline) {
+        mNotifPipeline = pipeline;
+        mNotifPipeline.addNotificationDismissInterceptor(mDismissInterceptor);
+        mNotifPipeline.addPreRenderFilter(mNotifFilter);
+        mBubbleController.addNotifCallback(mNotifCallback);
+    }
+
+    private final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            return mBubbleController.isBubbleNotificationSuppressedFromShade(entry);
+        }
+    };
+
+    private final NotifDismissInterceptor mDismissInterceptor = new NotifDismissInterceptor() {
+        @Override
+        public String getName() {
+            return TAG;
+        }
+
+        @Override
+        public void setCallback(OnEndDismissInterception callback) {
+            mOnEndDismissInterception = callback;
+        }
+
+        @Override
+        public boolean shouldInterceptDismissal(NotificationEntry entry) {
+            // TODO: b/149041810 add support for intercepting app-cancelled bubble notifications
+            // for experimental bubbles
+            if (mBubbleController.handleDismissalInterception(entry)) {
+                mInterceptedDismissalEntries.add(entry.getKey());
+                return true;
+            } else {
+                mInterceptedDismissalEntries.remove(entry.getKey());
+                return false;
+            }
+        }
+
+        @Override
+        public void cancelDismissInterception(NotificationEntry entry) {
+            mInterceptedDismissalEntries.remove(entry.getKey());
+        }
+    };
+
+    private final BubbleController.NotifCallback mNotifCallback =
+            new BubbleController.NotifCallback() {
+        @Override
+        public void removeNotification(NotificationEntry entry, int reason) {
+            if (isInterceptingDismissal(entry)) {
+                mInterceptedDismissalEntries.remove(entry.getKey());
+                mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
+                        createDismissedByUserStats(entry));
+            } else if (mNotifPipeline.getActiveNotifs().contains(entry)) {
+                // Bubbles are hiding the notifications from the shade, but the bubble was
+                // deleted; therefore, the notification should be cancelled as if it were a user
+                // dismissal (this won't re-enter handleInterceptDimissal because Bubbles
+                // will have already marked it as no longer a bubble)
+                mNotifCollection.dismissNotification(entry, createDismissedByUserStats(entry));
+            }
+        }
+
+        @Override
+        public void invalidateNotifications(String reason) {
+            mNotifFilter.invalidateList();
+        }
+
+        @Override
+        public void maybeCancelSummary(NotificationEntry entry) {
+            // no-op
+        }
+    };
+
+    private boolean isInterceptingDismissal(NotificationEntry entry) {
+        return mInterceptedDismissalEntries.contains(entry.getKey());
+    }
+
+    private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) {
+        return new DismissedByUserStats(
+                DISMISSAL_OTHER,
+                DISMISS_SENTIMENT_UNKNOWN,
+                NotificationVisibility.obtain(entry.getKey(),
+                        entry.getRanking().getRank(),
+                        mNotifPipeline.getActiveNotifs().size(),
+                        true, // was visible as a bubble
+                        NotificationLogger.getNotificationLocation(entry))
+        );
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
index 0a1e09f..7a9547c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -53,6 +53,7 @@
             RankingCoordinator rankingCoordinator,
             ForegroundCoordinator foregroundCoordinator,
             DeviceProvisionedCoordinator deviceProvisionedCoordinator,
+            BubbleCoordinator bubbleCoordinator,
             PreparationCoordinator preparationCoordinator) {
         dumpController.registerDumpable(TAG, this);
 
@@ -61,6 +62,7 @@
         mCoordinators.add(rankingCoordinator);
         mCoordinators.add(foregroundCoordinator);
         mCoordinators.add(deviceProvisionedCoordinator);
+        mCoordinators.add(bubbleCoordinator);
         if (featureFlags.isNewNotifPipelineRenderingEnabled()) {
             mCoordinators.add(preparationCoordinator);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 41314b8..1e5946a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -22,8 +22,6 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
-import com.android.systemui.statusbar.notification.logging.NotifEvent;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -42,13 +40,15 @@
 public class PreparationCoordinator implements Coordinator {
     private static final String TAG = "PreparationCoordinator";
 
-    private final NotifLog mNotifLog;
+    private final PreparationCoordinatorLogger mLogger;
     private final NotifInflater mNotifInflater;
     private final List<NotificationEntry> mPendingNotifications = new ArrayList<>();
 
     @Inject
-    public PreparationCoordinator(NotifLog notifLog, NotifInflaterImpl notifInflater) {
-        mNotifLog = notifLog;
+    public PreparationCoordinator(
+            PreparationCoordinatorLogger logger,
+            NotifInflaterImpl notifInflater) {
+        mLogger = logger;
         mNotifInflater = notifInflater;
         mNotifInflater.setInflationCallback(mInflationCallback);
     }
@@ -106,7 +106,7 @@
             new NotifInflater.InflationCallback() {
         @Override
         public void onInflationFinished(NotificationEntry entry) {
-            mNotifLog.log(NotifEvent.INFLATED, entry);
+            mLogger.logNotifInflated(entry.getKey());
             mPendingNotifications.remove(entry);
             mNotifInflatingFilter.invalidateList();
         }
@@ -123,7 +123,7 @@
     }
 
     private void abortInflation(NotificationEntry entry, String reason) {
-        mNotifLog.log(NotifEvent.INFLATION_ABORTED, reason);
+        mLogger.logInflationAborted(entry.getKey(), reason);
         entry.abortTask();
         mPendingNotifications.remove(entry);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
new file mode 100644
index 0000000..75e7bc9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationLog
+import javax.inject.Inject
+
+class PreparationCoordinatorLogger @Inject constructor(
+    @NotificationLog private val buffer: LogBuffer
+) {
+    fun logNotifInflated(key: String) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = key
+        }, {
+            "NOTIF INFLATED $str1"
+        })
+    }
+
+    fun logInflationAborted(key: String, reason: String) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = key
+            str2 = reason
+        }, {
+            "NOTIF INFLATION ABORTED $str1 reason=$str2"
+        })
+    }
+}
+
+private const val TAG = "PreparationCoordinator"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 59d82a1..ecf62db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -295,6 +295,7 @@
         }
         //TODO: Replace this API with RowContentBindParams directly
         row.setNeedsRedaction(mNotificationLockscreenUserManager.needsRedaction(entry));
+        params.rebindAllContentViews();
         mRowContentBindStage.requestRebind(entry, en -> {
             row.setUsesIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
             row.setUsesIncreasedHeadsUpHeight(useIncreasedHeadsUp);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 14e1503..dc7a50d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -69,6 +69,14 @@
         })
     }
 
+    fun logNotifDismissedIntercepted(key: String) {
+        buffer.log(TAG, INFO, {
+            str1 = key
+        }, {
+            "DISMISS INTERCEPTED $str1"
+        })
+    }
+
     fun logRankingMissing(key: String, rankingMap: RankingMap) {
         buffer.log(TAG, WARNING, { str1 = key }, { "Ranking update is missing ranking for $str1" })
         buffer.log(TAG, DEBUG, {}, { "Ranking map contents:" })
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java
new file mode 100644
index 0000000..3354ad1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifDismissInterceptor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * A way for coordinators to temporarily intercept a user-dismissed notification before a message
+ * is sent to system server to officially remove this notification.
+ * See {@link NotifCollection#addNotificationDismissInterceptor(NotifDismissInterceptor)}.
+ */
+public interface NotifDismissInterceptor {
+    /** Name to associate with this interceptor (for the purposes of debugging) */
+    String getName();
+
+    /**
+     * Called on the interceptor immediately after it has been registered. The interceptor should
+     * hang on to this callback and execute it whenever it no longer needs to intercept the
+     * dismissal of the notification.
+     */
+    void setCallback(OnEndDismissInterception callback);
+
+    /**
+     * Called by the NotifCollection whenever a notification has been dismissed (by the user).
+     * If the interceptor returns true, it is considered to be intercepting the notification.
+     * Intercepted notifications will not be sent to system server for removal until it is no
+     * longer being intercepted. However, the notification can still be cancelled by the app.
+     * This method is called on all interceptors even if earlier ones return true.
+     */
+    boolean shouldInterceptDismissal(NotificationEntry entry);
+
+
+    /**
+     * Called by the NotifCollection to inform a DismissInterceptor that its interception of a notif
+     * is no longer valid (usually because the notif has been removed by means other than the
+     * user dismissing the notification from the shade, or the notification has been updated). The
+     * interceptor should clean up any references it has to the notif in question.
+     */
+    void cancelDismissInterception(NotificationEntry entry);
+
+    /**
+     * Callback for notifying the NotifCollection that it no longer is intercepting the dismissal.
+     * If the end of this dismiss interception triggers a dismiss (ie: no other
+     * NotifDismissInterceptors are intercepting the entry), NotifCollection will use stats
+     * in the message sent to system server for the notification's dismissal.
+     */
+    interface OnEndDismissInterception {
+        void onEndDismissInterception(
+                NotifDismissInterceptor interceptor,
+                NotificationEntry entry,
+                DismissedByUserStats stats);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
deleted file mode 100644
index 9adceb7..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ /dev/null
@@ -1,212 +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.statusbar.notification.logging;
-
-import android.annotation.IntDef;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-
-import com.android.systemui.log.RichEvent;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
-import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * An event related to notifications. {@link NotifLog} stores and prints these events for debugging
- * and triaging purposes. We do not store a copy of the status bar notification nor ranking
- * here to mitigate memory usage.
- */
-public class NotifEvent extends RichEvent {
-    /**
-     * Initializes a rich event that includes an event type that matches with an index in the array
-     * getEventLabels().
-     */
-    public NotifEvent init(@EventType int type, StatusBarNotification sbn,
-            NotificationListenerService.Ranking ranking, String reason) {
-        StringBuilder extraInfo = new StringBuilder(reason);
-        if (sbn != null) {
-            extraInfo.append(" " + sbn.getKey());
-        }
-
-        if (ranking != null) {
-            extraInfo.append(" Ranking=");
-            extraInfo.append(ranking.getRank());
-        }
-        super.init(INFO, type, extraInfo.toString());
-        return this;
-    }
-
-    /**
-     * Event labels for ListBuilderEvents
-     * Index corresponds to an # in {@link EventType}
-     */
-    @Override
-    public String[] getEventLabels() {
-        assert (TOTAL_EVENT_LABELS
-                == (TOTAL_NEM_EVENT_TYPES
-                        + TOTAL_LIST_BUILDER_EVENT_TYPES
-                        + TOTAL_COALESCER_EVENT_TYPES));
-        return EVENT_LABELS;
-    }
-
-    /**
-     * @return if this event occurred in {@link ShadeListBuilder}
-     */
-    static boolean isListBuilderEvent(@EventType int type) {
-        return isBetweenInclusive(type, 0, TOTAL_LIST_BUILDER_EVENT_TYPES);
-    }
-
-    /**
-     * @return if this event occurred in {@link NotificationEntryManager}
-     */
-    static boolean isNemEvent(@EventType int type) {
-        return isBetweenInclusive(type, TOTAL_LIST_BUILDER_EVENT_TYPES,
-                TOTAL_LIST_BUILDER_EVENT_TYPES + TOTAL_NEM_EVENT_TYPES);
-    }
-
-    private static boolean isBetweenInclusive(int x, int a, int b) {
-        return x >= a && x <= b;
-    }
-
-    @IntDef({
-            // NotifListBuilder events:
-            WARN,
-            ON_BUILD_LIST,
-            START_BUILD_LIST,
-            DISPATCH_FINAL_LIST,
-            LIST_BUILD_COMPLETE,
-            PRE_GROUP_FILTER_INVALIDATED,
-            PROMOTER_INVALIDATED,
-            SECTION_INVALIDATED,
-            COMPARATOR_INVALIDATED,
-            PARENT_CHANGED,
-            FILTER_CHANGED,
-            PROMOTER_CHANGED,
-            PRE_RENDER_FILTER_INVALIDATED,
-
-            // NotificationEntryManager events:
-            NOTIF_ADDED,
-            NOTIF_REMOVED,
-            NOTIF_UPDATED,
-            FILTER,
-            SORT,
-            FILTER_AND_SORT,
-            NOTIF_VISIBILITY_CHANGED,
-            LIFETIME_EXTENDED,
-            REMOVE_INTERCEPTED,
-            INFLATION_ABORTED,
-            INFLATED,
-
-            // GroupCoalescer
-            COALESCED_EVENT,
-            EARLY_BATCH_EMIT,
-            EMIT_EVENT_BATCH
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface EventType {}
-
-    private static final String[] EVENT_LABELS =
-            new String[]{
-                    // NotifListBuilder labels:
-                    "Warning",
-                    "OnBuildList",
-                    "StartBuildList",
-                    "DispatchFinalList",
-                    "ListBuildComplete",
-                    "FilterInvalidated",
-                    "PromoterInvalidated",
-                    "SectionInvalidated",
-                    "ComparatorInvalidated",
-                    "ParentChanged",
-                    "FilterChanged",
-                    "PromoterChanged",
-                    "FinalFilterInvalidated",
-                    "SectionerChanged",
-
-                    // NEM event labels:
-                    "NotifAdded",
-                    "NotifRemoved",
-                    "NotifUpdated",
-                    "Filter",
-                    "Sort",
-                    "FilterAndSort",
-                    "NotifVisibilityChanged",
-                    "LifetimeExtended",
-                    "RemoveIntercepted",
-                    "InflationAborted",
-                    "Inflated",
-
-                    // GroupCoalescer labels:
-                    "CoalescedEvent",
-                    "EarlyBatchEmit",
-                    "EmitEventBatch",
-                    "BatchMaxTimeout"
-            };
-
-    private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
-
-    /**
-     * Events related to {@link ShadeListBuilder}
-     */
-    public static final int WARN = 0;
-    public static final int ON_BUILD_LIST = 1;
-    public static final int START_BUILD_LIST = 2;
-    public static final int DISPATCH_FINAL_LIST = 3;
-    public static final int LIST_BUILD_COMPLETE = 4;
-    public static final int PRE_GROUP_FILTER_INVALIDATED = 5;
-    public static final int PROMOTER_INVALIDATED = 6;
-    public static final int SECTION_INVALIDATED = 7;
-    public static final int COMPARATOR_INVALIDATED = 8;
-    public static final int PARENT_CHANGED = 9;
-    public static final int FILTER_CHANGED = 10;
-    public static final int PROMOTER_CHANGED = 11;
-    public static final int PRE_RENDER_FILTER_INVALIDATED = 12;
-    public static final int SECTION_CHANGED = 13;
-    private static final int TOTAL_LIST_BUILDER_EVENT_TYPES = 14;
-
-    /**
-     * Events related to {@link NotificationEntryManager}
-     */
-    private static final int NEM_EVENT_START_INDEX = TOTAL_LIST_BUILDER_EVENT_TYPES;
-    public static final int NOTIF_ADDED = NEM_EVENT_START_INDEX;
-    public static final int NOTIF_REMOVED = NEM_EVENT_START_INDEX + 1;
-    public static final int NOTIF_UPDATED = NEM_EVENT_START_INDEX + 2;
-    public static final int FILTER = NEM_EVENT_START_INDEX + 3;
-    public static final int SORT = NEM_EVENT_START_INDEX + 4;
-    public static final int FILTER_AND_SORT = NEM_EVENT_START_INDEX + 5;
-    public static final int NOTIF_VISIBILITY_CHANGED = NEM_EVENT_START_INDEX + 6;
-    public static final int LIFETIME_EXTENDED = NEM_EVENT_START_INDEX + 7;
-    // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor}
-    public static final int REMOVE_INTERCEPTED = NEM_EVENT_START_INDEX + 8;
-    public static final int INFLATION_ABORTED = NEM_EVENT_START_INDEX + 9;
-    public static final int INFLATED = NEM_EVENT_START_INDEX + 10;
-    private static final int TOTAL_NEM_EVENT_TYPES = 11;
-
-    /**
-     * Events related to {@link GroupCoalescer}
-     */
-    private static final int COALESCER_EVENT_START_INDEX = NEM_EVENT_START_INDEX
-            + TOTAL_NEM_EVENT_TYPES;
-    public static final int COALESCED_EVENT = COALESCER_EVENT_START_INDEX;
-    public static final int EARLY_BATCH_EMIT = COALESCER_EVENT_START_INDEX + 1;
-    public static final int EMIT_EVENT_BATCH = COALESCER_EVENT_START_INDEX + 2;
-    public static final int BATCH_MAX_TIMEOUT = COALESCER_EVENT_START_INDEX + 3;
-    private static final int TOTAL_COALESCER_EVENT_TYPES = 3;
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
deleted file mode 100644
index 299d628..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java
+++ /dev/null
@@ -1,115 +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.statusbar.notification.logging;
-
-import android.os.SystemProperties;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.StatusBarNotification;
-
-import com.android.systemui.DumpController;
-import com.android.systemui.log.SysuiLog;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Logs systemui notification events for debugging and triaging purposes. Logs are dumped in
- * bugreports or on demand:
- *      adb shell dumpsys activity service com.android.systemui/.SystemUIService \
- *      dependency DumpController NotifLog
- */
-@Singleton
-public class NotifLog extends SysuiLog<NotifEvent> {
-    private static final String TAG = "NotifLog";
-    private static final boolean SHOW_NEM_LOGS =
-            SystemProperties.getBoolean("persist.sysui.log.notif.nem", true);
-    private static final boolean SHOW_LIST_BUILDER_LOGS =
-            SystemProperties.getBoolean("persist.sysui.log.notif.listbuilder", true);
-
-    private static final int MAX_DOZE_DEBUG_LOGS = 400;
-    private static final int MAX_DOZE_LOGS = 50;
-
-    private NotifEvent mRecycledEvent;
-
-    @Inject
-    public NotifLog(DumpController dumpController) {
-        super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS);
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a notification, ranking and message.
-     * Uses the last recycled event if available.
-     * @return true if successfully logged, else false
-     */
-    public void log(@NotifEvent.EventType int eventType,
-            StatusBarNotification sbn, Ranking ranking, String msg) {
-        if (!mEnabled
-                || (NotifEvent.isListBuilderEvent(eventType) && !SHOW_LIST_BUILDER_LOGS)
-                || (NotifEvent.isNemEvent(eventType) && !SHOW_NEM_LOGS)) {
-            return;
-        }
-
-        if (mRecycledEvent != null) {
-            mRecycledEvent = log(mRecycledEvent.init(eventType, sbn, ranking, msg));
-        } else {
-            mRecycledEvent = log(new NotifEvent().init(eventType, sbn, ranking, msg));
-        }
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with no extra information aside from the event type
-     */
-    public void log(@NotifEvent.EventType int eventType) {
-        log(eventType, null, null, "");
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a message
-     */
-    public void log(@NotifEvent.EventType int eventType, String msg) {
-        log(eventType, null, null, msg);
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a entry
-     */
-    public void log(@NotifEvent.EventType int eventType, NotificationEntry entry) {
-        log(eventType, entry.getSbn(), entry.getRanking(), "");
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a NotificationEntry and message
-     */
-    public void log(@NotifEvent.EventType int eventType, NotificationEntry entry, String msg) {
-        log(eventType, entry.getSbn(), entry.getRanking(), msg);
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a notification and message
-     */
-    public void log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) {
-        log(eventType, sbn, null, msg);
-    }
-
-    /**
-     * Logs a {@link NotifEvent} with a ranking and message
-     */
-    public void log(@NotifEvent.EventType int eventType, Ranking ranking, String msg) {
-        log(eventType, null, ranking, msg);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index 8280a63..5170d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -123,6 +123,14 @@
     }
 
     /**
+     * Request that all content views be rebound. This may happen if, for example, the underlying
+     * layout has changed.
+     */
+    public void rebindAllContentViews() {
+        mDirtyContentViews = mContentViews;
+    }
+
+    /**
      * Clears all dirty content views so that they no longer need to be rebound.
      */
     void clearDirtyContentViews() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index db692c8..44a3204 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -422,9 +422,7 @@
     }
 
     private void updateDisplaySize() {
-        mContext.getSystemService(DisplayManager.class)
-                .getDisplay(mDisplayId)
-                .getRealSize(mDisplaySize);
+        mContext.getDisplay().getRealSize(mDisplaySize);
         if (mEdgeBackPlugin != null) {
             mEdgeBackPlugin.setDisplaySize(mDisplaySize);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a6498d5..4f01cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2466,10 +2466,6 @@
             pw.println("  mHeadsUpManager: null");
         }
 
-        if (mBubbleController != null) {
-            mBubbleController.dump(fd, pw, args);
-        }
-
         if (mLightBarController != null) {
             mLightBarController.dump(fd, pw, args);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 830b50e..8231f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -30,5 +30,6 @@
 
     interface Callback {
         void onHotspotChanged(boolean enabled, int numDevices);
+        default void onHotspotAvailabilityChanged(boolean available) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index df9c3f4..d090404 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.TetheringManager;
 import android.net.wifi.WifiClient;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
@@ -26,6 +27,8 @@
 import android.os.UserManager;
 import android.util.Log;
 
+import com.android.internal.util.ConcurrentUtils;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 
 import java.io.FileDescriptor;
@@ -46,36 +49,63 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
-    private final ConnectivityManager mConnectivityManager;
+    private final TetheringManager mTetheringManager;
     private final WifiManager mWifiManager;
     private final Handler mMainHandler;
     private final Context mContext;
 
     private int mHotspotState;
     private volatile int mNumConnectedDevices;
+    private volatile boolean mIsTetheringSupported;
+    private volatile boolean mHasTetherableWifiRegexs;
     private boolean mWaitingForTerminalState;
 
+    private TetheringManager.TetheringEventCallback mTetheringCallback =
+            new TetheringManager.TetheringEventCallback() {
+                @Override
+                public void onTetheringSupported(boolean supported) {
+                    super.onTetheringSupported(supported);
+                    if (mIsTetheringSupported != supported) {
+                        mIsTetheringSupported = supported;
+                        fireHotspotAvailabilityChanged();
+                    }
+                }
+
+                @Override
+                public void onTetherableInterfaceRegexpsChanged(
+                        TetheringManager.TetheringInterfaceRegexps reg) {
+                    super.onTetherableInterfaceRegexpsChanged(reg);
+                    final boolean newValue = reg.getTetherableWifiRegexs().size() != 0;
+                    if (mHasTetherableWifiRegexs != newValue) {
+                        mHasTetherableWifiRegexs = newValue;
+                        fireHotspotAvailabilityChanged();
+                    }
+                }
+            };
+
     /**
      * Controller used to retrieve information related to a hotspot.
      */
     @Inject
-    public HotspotControllerImpl(Context context, @Main Handler mainHandler) {
+    public HotspotControllerImpl(Context context, @Main Handler mainHandler,
+            @Background Handler backgroundHandler) {
         mContext = context;
-        mConnectivityManager =
-                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mTetheringManager = context.getSystemService(TetheringManager.class);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
         mMainHandler = mainHandler;
+        mTetheringManager.registerTetheringEventCallback(
+                new HandlerExecutor(backgroundHandler), mTetheringCallback);
     }
 
     @Override
     public boolean isHotspotSupported() {
-        return mConnectivityManager.isTetheringSupported()
-                && mConnectivityManager.getTetherableWifiRegexs().length != 0
+        return mIsTetheringSupported && mHasTetherableWifiRegexs
                 && UserManager.get(mContext).isUserAdmin(ActivityManager.getCurrentUser());
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("HotspotController state:");
+        pw.print("  available="); pw.println(isHotspotSupported());
         pw.print("  mHotspotState="); pw.println(stateToString(mHotspotState));
         pw.print("  mNumConnectedDevices="); pw.println(mNumConnectedDevices);
         pw.print("  mWaitingForTerminalState="); pw.println(mWaitingForTerminalState);
@@ -152,17 +182,18 @@
         if (enabled) {
             mWaitingForTerminalState = true;
             if (DEBUG) Log.d(TAG, "Starting tethering");
-            mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, false,
-                    new ConnectivityManager.OnStartTetheringCallback() {
+            mTetheringManager.startTethering(ConnectivityManager.TETHERING_WIFI,
+                    ConcurrentUtils.DIRECT_EXECUTOR,
+                    new TetheringManager.StartTetheringCallback() {
                         @Override
-                        public void onTetheringFailed() {
+                        public void onTetheringFailed(final int result) {
                             if (DEBUG) Log.d(TAG, "onTetheringFailed");
                             maybeResetSoftApState();
                             fireHotspotChangedCallback();
                         }
                     });
         } else {
-            mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+            mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
         }
     }
 
@@ -177,10 +208,25 @@
      * (as it can be blocked).
      */
     private void fireHotspotChangedCallback() {
+        List<Callback> list;
         synchronized (mCallbacks) {
-            for (Callback callback : mCallbacks) {
-                callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
-            }
+            list = new ArrayList<>(mCallbacks);
+        }
+        for (Callback callback : list) {
+            callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices);
+        }
+    }
+
+    /**
+     * Sends a hotspot available changed callback.
+     */
+    private void fireHotspotAvailabilityChanged() {
+        List<Callback> list;
+        synchronized (mCallbacks) {
+            list = new ArrayList<>(mCallbacks);
+        }
+        for (Callback callback : list) {
+            callback.onHotspotAvailabilityChanged(isHotspotSupported());
         }
     }
 
@@ -206,7 +252,7 @@
         switch (mHotspotState) {
             case WifiManager.WIFI_AP_STATE_FAILED:
                 // TODO(b/110697252): must be called to reset soft ap state after failure
-                mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
+                mTetheringManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
                 // Fall through
             case WifiManager.WIFI_AP_STATE_ENABLED:
             case WifiManager.WIFI_AP_STATE_DISABLED:
diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
index cfd77be..f4157f2 100644
--- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt
@@ -901,6 +901,23 @@
             verboseLogging = debug
         }
 
+        /**
+         * Estimates the end value of a fling that starts at the given value using the provided
+         * start velocity and fling configuration.
+         *
+         * This is only an estimate. Fling animations use a timing-based physics simulation that is
+         * non-deterministic, so this exact value may not be reached.
+         */
+        @JvmStatic
+        fun estimateFlingEndValue(
+            startValue: Float,
+            startVelocity: Float,
+            flingConfig: FlingConfig
+        ): Float {
+            val distance = startVelocity / (flingConfig.friction * FLING_FRICTION_SCALAR_MULTIPLIER)
+            return Math.min(flingConfig.max, Math.max(flingConfig.min, startValue + distance))
+        }
+
         @JvmStatic
         fun getReadablePropertyName(property: FloatPropertyCompat<*>): String {
             return when (property) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 1954b39..0e9a245 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -39,7 +39,7 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.ViewUtils;
-import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -54,7 +54,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 
 @RunWithLooper
 @RunWith(AndroidTestingRunner.class)
@@ -77,8 +76,8 @@
     private KeyguardSecurityCallback mKeyguardCallback;
     @Mock
     private KeyguardUpdateMonitor mUpdateMonitor;
-    @Spy
-    private StubTransaction mTransaction;
+    @Mock
+    private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
 
     @Before
     public void setUp() {
@@ -97,21 +96,20 @@
         when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient);
 
         mTestController = new AdminSecondaryLockScreenController(
-                mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler, mTransaction);
+                mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler);
     }
 
     @Test
     public void testShow() throws Exception {
         doAnswer(invocation -> {
             IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
-            callback.onSurfaceControlCreated(new SurfaceControl());
+            callback.onRemoteContentReady(mSurfacePackage);
             return null;
         }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
 
         mTestController.show(mServiceIntent);
 
         verifySurfaceReady();
-        verify(mTransaction).reparent(any(), any());
         assertThat(mContext.isBound(mComponentName)).isTrue();
     }
 
@@ -133,7 +131,7 @@
         // Show the view first, then hide.
         doAnswer(invocation -> {
             IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1];
-            callback.onSurfaceControlCreated(new SurfaceControl());
+            callback.onRemoteContentReady(mSurfacePackage);
             return null;
         }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class));
 
@@ -189,19 +187,4 @@
         verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID);
         assertThat(mContext.isBound(mComponentName)).isFalse();
     }
-
-    /**
-     * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit testing to
-     * avoid calls to native code.
-     */
-    private class StubTransaction extends SurfaceControl.Transaction {
-        @Override
-        public void apply() {
-        }
-
-        @Override
-        public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) {
-            return this;
-        }
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
index 486aac8..c6c7b87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java
@@ -178,6 +178,20 @@
     }
 
     @Test
+    public void testCredentialUI_disablesClickingOnBackground() {
+        // In the credential view, clicking on the background (to cancel authentication) is not
+        // valid. Thus, the listener should be null, and it should not be in the accessibility
+        // hierarchy.
+        initializeContainer(Authenticators.DEVICE_CREDENTIAL);
+
+        mAuthContainer.onAttachedToWindowInternal();
+
+        verify(mAuthContainer.mBackgroundView).setOnClickListener(eq(null));
+        verify(mAuthContainer.mBackgroundView).setImportantForAccessibility(
+                eq(View.IMPORTANT_FOR_ACCESSIBILITY_NO));
+    }
+
+    @Test
     public void testLayoutParams_hasSecureWindowFlag() {
         final IBinder windowToken = mock(IBinder.class);
         final WindowManager.LayoutParams layoutParams =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index d7f0f50..c3b55e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -20,8 +20,7 @@
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
-
-import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -34,6 +33,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -55,10 +55,12 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.DumpController;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -68,6 +70,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -81,7 +84,6 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.util.InjectionInflationController;
 
@@ -93,6 +95,13 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
+/**
+ * Tests the NotificationEntryManager setup with BubbleController.
+ * The {@link NotifPipeline} setup with BubbleController is tested in
+ * {@link NewNotifPipelineBubbleControllerTest}.
+ */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -152,9 +161,13 @@
     @Mock
     private ShadeController mShadeController;
     @Mock
-    private RemoteInputUriController mRemoteInputUriController;
-    @Mock
     private NotificationRowComponent mNotificationRowComponent;
+    @Mock
+    private NotifPipeline mNotifPipeline;
+    @Mock
+    private FeatureFlags mFeatureFlagsOldPipeline;
+    @Mock
+    private DumpController mDumpController;
 
     private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
     private BubbleData mBubbleData;
@@ -216,6 +229,7 @@
                 mock(HeadsUpManager.class),
                 mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
         mBubbleData = new BubbleData(mContext);
+        when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mBubbleController = new TestableBubbleController(mContext,
                 mNotificationShadeWindowController,
                 mStatusBarStateController,
@@ -227,7 +241,9 @@
                 mLockscreenUserManager,
                 mNotificationGroupManager,
                 mNotificationEntryManager,
-                mRemoteInputUriController);
+                mNotifPipeline,
+                mFeatureFlagsOldPipeline,
+                mDumpController);
         mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
         mBubbleController.setExpandListener(mBubbleExpandListener);
 
@@ -265,7 +281,7 @@
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
 
         mBubbleController.removeBubble(
-                mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
         assertFalse(mNotificationShadeWindowController.getBubblesShowing());
         assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
         verify(mNotificationEntryManager, times(2)).updateNotifications(anyString());
@@ -286,12 +302,12 @@
 
         // Now remove the bubble
         mBubbleController.removeBubble(
-                mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
 
         // Since the notif is dismissed, once the bubble is removed, performRemoveNotification gets
         // called to really remove the notif
         verify(mNotificationEntryManager, times(1)).performRemoveNotification(
-                mRow.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
+                eq(mRow.getEntry().getSbn()), anyInt());
         assertFalse(mBubbleController.hasBubbles());
     }
 
@@ -471,7 +487,7 @@
                 mRow2.getEntry()));
 
         // Dismiss currently expanded
-        mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
+        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
                 BubbleController.DISMISS_USER_GESTURE);
         verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
 
@@ -480,7 +496,7 @@
         verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
 
         // Dismiss that one
-        mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
+        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
                 BubbleController.DISMISS_USER_GESTURE);
 
         // Make sure state changes and collapse happens
@@ -608,7 +624,7 @@
     @Test
     public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
         mBubbleController.updateBubble(mRow.getEntry());
-        mBubbleController.removeBubble(mRow.getEntry().getKey(), BubbleController.DISMISS_AGED);
+        mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
         verify(mDeleteIntent, never()).send();
     }
 
@@ -616,7 +632,7 @@
     public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
         mBubbleController.updateBubble(mRow.getEntry());
         mBubbleController.removeBubble(
-                mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
         verify(mDeleteIntent, times(1)).send();
     }
 
@@ -653,11 +669,22 @@
 
         // Cancels always remove so no need to intercept
         assertFalse(intercepted);
+    }
+
+    @Test
+    public void testRemoveBubble_entryListenerRemove() {
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Removes the notification
+        mEntryListener.onEntryRemoved(mRow.getEntry(), null, false);
         assertFalse(mBubbleController.hasBubbles());
     }
 
     @Test
-    public void removeBubble_fails_clearAll()  {
+    public void removeBubble_clearAllIntercepted()  {
         mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
@@ -673,14 +700,10 @@
         // Should update show in shade state
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
-
-        verify(mNotificationEntryManager, never()).performRemoveNotification(
-                any(), anyInt());
-        assertTrue(mBubbleController.hasBubbles());
     }
 
     @Test
-    public void removeBubble_fails_userDismissNotif() {
+    public void removeBubble_userDismissNotifIntercepted() {
         mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
@@ -696,10 +719,6 @@
         // Should update show in shade state
         assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
-
-        verify(mNotificationEntryManager, never()).performRemoveNotification(
-                any(), anyInt());
-        assertTrue(mBubbleController.hasBubbles());
     }
 
     @Test
@@ -713,7 +732,7 @@
 
         // Dismiss the bubble
         mBubbleController.removeBubble(
-                mRow.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
         assertFalse(mBubbleController.hasBubbles());
 
         // Dismiss the notification
@@ -771,6 +790,74 @@
                 mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
     }
 
+    @Test
+    public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
+        // GIVEN a group summary with a bubble child
+        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+        groupSummary.addChildNotification(groupedBubble);
+        assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+        // WHEN the summary is dismissed
+        mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+        // THEN the summary and bubbled child are suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getEntry()));
+        assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
+    }
+
+    @Test
+    public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
+        // GIVEN a group summary with a bubble child
+        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+        groupSummary.addChildNotification(groupedBubble);
+        assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+        // GIVEN the summary is dismissed
+        mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+        // WHEN the summary is cancelled by the app
+        mEntryListener.onEntryRemoved(groupSummary.getEntry(), null, true);
+
+        // THEN the summary and its children are removed from bubble data
+        assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+        assertFalse(mBubbleData.isSummarySuppressed(
+                groupSummary.getEntry().getSbn().getGroupKey()));
+    }
+
+    @Test
+    public void testSummaryDismissal_marksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
+            throws Exception {
+        // GIVEN a group summary with two (non-bubble) children and one bubble child
+        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
+        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onPendingEntryAdded(groupedBubble.getEntry());
+        groupSummary.addChildNotification(groupedBubble);
+
+        // WHEN the summary is dismissed
+        mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+        // THEN only the NON-bubble children are dismissed
+        List<ExpandableNotificationRow> childrenRows = groupSummary.getNotificationChildren();
+        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+                childrenRows.get(0).getEntry().getSbn(), REASON_GROUP_SUMMARY_CANCELED);
+        verify(mNotificationEntryManager, times(1)).performRemoveNotification(
+                childrenRows.get(1).getEntry().getSbn(), REASON_GROUP_SUMMARY_CANCELED);
+        verify(mNotificationEntryManager, never()).performRemoveNotification(
+                eq(groupedBubble.getEntry().getSbn()), anyInt());
+
+        // THEN the bubble child is suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getEntry()));
+
+        // THEN the summary is removed from GroupManager
+        verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
+    }
+
     static class TestableBubbleController extends BubbleController {
         // Let's assume surfaces can be synchronized immediately.
         TestableBubbleController(Context context,
@@ -784,12 +871,14 @@
                 NotificationLockscreenUserManager lockscreenUserManager,
                 NotificationGroupManager groupManager,
                 NotificationEntryManager entryManager,
-                RemoteInputUriController remoteInputUriController) {
+                NotifPipeline notifPipeline,
+                FeatureFlags featureFlags,
+                DumpController dumpController) {
             super(context,
                     notificationShadeWindowController, statusBarStateController, shadeController,
                     data, Runnable::run, configurationController, interruptionStateProvider,
                     zenModeController, lockscreenUserManager, groupManager, entryManager,
-                    remoteInputUriController);
+                     notifPipeline, featureFlags, dumpController);
             setInflateSynchronously(true);
         }
     }
@@ -806,7 +895,7 @@
     }
 
     /**
-     * Sets the bubble metadata flags for this entry. These flags are normally set by
+     * Sets the bubble metadata flags for this entry. These ]flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
      * go through that path so we set them explicitly when testing.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
new file mode 100644
index 0000000..72405fc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -0,0 +1,842 @@
+/*
+ * 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.bubbles;
+
+import static android.app.Notification.FLAG_BUBBLE;
+import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+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 android.app.IActivityManager;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.face.FaceManager;
+import android.service.notification.ZenModeConfig;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.WindowManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.systemui.DumpController;
+import com.android.systemui.SystemUIFactory;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.SuperStatusBarViewFactory;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Tests the NotifPipeline setup with BubbleController.
+ * The NotificationEntryManager setup with BubbleController is tested in
+ * {@link BubbleControllerTest}.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase {
+    @Mock
+    private NotificationEntryManager mNotificationEntryManager;
+    @Mock
+    private NotificationGroupManager mNotificationGroupManager;
+    @Mock
+    private BubbleController.NotifCallback mNotifCallback;
+    @Mock
+    private WindowManager mWindowManager;
+    @Mock
+    private IActivityManager mActivityManager;
+    @Mock
+    private DozeParameters mDozeParameters;
+    @Mock
+    private ConfigurationController mConfigurationController;
+    @Mock
+    private ZenModeController mZenModeController;
+    @Mock
+    private ZenModeConfig mZenModeConfig;
+    @Mock
+    private FaceManager mFaceManager;
+    @Mock
+    private NotificationLockscreenUserManager mLockscreenUserManager;
+    @Mock
+    private SysuiStatusBarStateController mStatusBarStateController;
+    @Mock
+    private KeyguardBypassController mKeyguardBypassController;
+
+    @Captor
+    private ArgumentCaptor<NotifCollectionListener> mNotifListenerCaptor;
+
+    private TestableBubbleController mBubbleController;
+    private NotificationShadeWindowController mNotificationShadeWindowController;
+    private NotifCollectionListener mEntryListener;
+
+    private NotificationTestHelper mNotificationTestHelper;
+    private ExpandableNotificationRow mRow;
+    private ExpandableNotificationRow mRow2;
+    private ExpandableNotificationRow mNonBubbleNotifRow;
+
+    @Mock
+    private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
+    @Mock
+    private BubbleController.BubbleExpandListener mBubbleExpandListener;
+    @Mock
+    private PendingIntent mDeleteIntent;
+    @Mock
+    private SysuiColorExtractor mColorExtractor;
+    @Mock
+    ColorExtractor.GradientColors mGradientColors;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private ShadeController mShadeController;
+    @Mock
+    private NotificationRowComponent mNotificationRowComponent;
+    @Mock
+    private NotifPipeline mNotifPipeline;
+    @Mock
+    private FeatureFlags mFeatureFlagsNewPipeline;
+    @Mock
+    private DumpController mDumpController;
+
+    private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+    private BubbleData mBubbleData;
+
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mTestableLooper = TestableLooper.get(this);
+
+        mContext.addMockSystemService(FaceManager.class, mFaceManager);
+        when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors);
+
+        mSuperStatusBarViewFactory = new SuperStatusBarViewFactory(mContext,
+                new InjectionInflationController(SystemUIFactory.getInstance().getRootComponent()),
+                new NotificationRowComponent.Builder() {
+                    @Override
+                    public NotificationRowComponent.Builder activatableNotificationView(
+                            ActivatableNotificationView view) {
+                        return this;
+                    }
+
+                    @Override
+                    public NotificationRowComponent build() {
+                        return mNotificationRowComponent;
+                    }
+                });
+
+        // Bubbles get added to status bar window view
+        mNotificationShadeWindowController = new NotificationShadeWindowController(mContext,
+                mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
+                mConfigurationController, mKeyguardBypassController, mColorExtractor,
+                mSuperStatusBarViewFactory);
+        mNotificationShadeWindowController.attach();
+
+        // Need notifications for bubbles
+        mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
+        mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
+        mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
+        mNonBubbleNotifRow = mNotificationTestHelper.createRow();
+
+        mZenModeConfig.suppressedVisualEffects = 0;
+        when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
+
+        TestableNotificationInterruptionStateProvider interruptionStateProvider =
+                new TestableNotificationInterruptionStateProvider(mContext,
+                        mock(NotificationFilter.class),
+                        mock(StatusBarStateController.class),
+                        mock(BatteryController.class));
+        interruptionStateProvider.setUpWithPresenter(
+                mock(NotificationPresenter.class),
+                mock(HeadsUpManager.class),
+                mock(NotificationInterruptionStateProvider.HeadsUpSuppressor.class));
+        mBubbleData = new BubbleData(mContext);
+        when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true);
+        mBubbleController = new TestableBubbleController(mContext,
+                mNotificationShadeWindowController,
+                mStatusBarStateController,
+                mShadeController,
+                mBubbleData,
+                mConfigurationController,
+                interruptionStateProvider,
+                mZenModeController,
+                mLockscreenUserManager,
+                mNotificationGroupManager,
+                mNotificationEntryManager,
+                mNotifPipeline,
+                mFeatureFlagsNewPipeline,
+                mDumpController);
+        mBubbleController.addNotifCallback(mNotifCallback);
+        mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
+        mBubbleController.setExpandListener(mBubbleExpandListener);
+
+        // Get a reference to the BubbleController's entry listener
+        verify(mNotifPipeline, atLeastOnce())
+                .addCollectionListener(mNotifListenerCaptor.capture());
+        mEntryListener = mNotifListenerCaptor.getValue();
+    }
+
+    @Test
+    public void testAddBubble() {
+        mBubbleController.updateBubble(mRow.getEntry());
+        assertTrue(mBubbleController.hasBubbles());
+
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+    }
+
+    @Test
+    public void testHasBubbles() {
+        assertFalse(mBubbleController.hasBubbles());
+        mBubbleController.updateBubble(mRow.getEntry());
+        assertTrue(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void testRemoveBubble() {
+        mBubbleController.updateBubble(mRow.getEntry());
+        assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+        assertTrue(mBubbleController.hasBubbles());
+        verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+
+        mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+        assertFalse(mNotificationShadeWindowController.getBubblesShowing());
+        assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+        verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+    }
+
+    @Test
+    public void testRemoveBubble_withDismissedNotif() {
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+        // Make it look like dismissed notif
+        mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
+
+        // Now remove the bubble
+        mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+
+        // Since the notif is dismissed, once the bubble is removed, removeNotification gets
+        // called to really remove the notif
+        verify(mNotifCallback, times(1)).removeNotification(eq(mRow.getEntry()), anyInt());
+        assertFalse(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void testDismissStack() {
+        mBubbleController.updateBubble(mRow.getEntry());
+        verify(mNotifCallback, times(1)).invalidateNotifications(anyString());
+        assertNotNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+        mBubbleController.updateBubble(mRow2.getEntry());
+        verify(mNotifCallback,  times(2)).invalidateNotifications(anyString());
+        assertNotNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+        assertTrue(mBubbleController.hasBubbles());
+
+        mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+        assertFalse(mNotificationShadeWindowController.getBubblesShowing());
+        verify(mNotifCallback, times(3)).invalidateNotifications(anyString());
+        assertNull(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+        assertNull(mBubbleData.getBubbleWithKey(mRow2.getEntry().getKey()));
+    }
+
+    @Test
+    public void testExpandCollapseStack() {
+        assertFalse(mBubbleController.isStackExpanded());
+
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // We should have bubbles & their notifs should not be suppressed
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+
+        // Expand the stack
+        BubbleStackView stackView = mBubbleController.getStackView();
+        mBubbleController.expandStack();
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+        assertTrue(mNotificationShadeWindowController.getBubbleExpanded());
+
+        // Make sure the notif is suppressed
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+        // Collapse
+        mBubbleController.collapseStack();
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
+        assertFalse(mBubbleController.isStackExpanded());
+        assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
+    }
+
+    @Test
+    public void testCollapseAfterChangingExpandedBubble() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mEntryListener.onEntryAdded(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow2.getEntry());
+
+        // We should have bubbles & their notifs should not be suppressed
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow2.getEntry()));
+
+        // Expand
+        BubbleStackView stackView = mBubbleController.getStackView();
+        mBubbleController.expandStack();
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+
+        // Last added is the one that is expanded
+        assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow2.getEntry()));
+
+        // Switch which bubble is expanded
+        mBubbleController.selectBubble(mRow.getEntry().getKey());
+        mBubbleData.setExpanded(true);
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        // collapse for previous bubble
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
+        // expand for selected bubble
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+        // Collapse
+        mBubbleController.collapseStack();
+        assertFalse(mBubbleController.isStackExpanded());
+    }
+
+    @Test
+    public void testExpansionRemovesShowInShadeAndDot() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // We should have bubbles & their notifs should not be suppressed
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+
+        mTestableLooper.processAllMessages();
+        assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+        // Expand
+        mBubbleController.expandStack();
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+        // Notif is suppressed after expansion
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        // Notif shouldn't show dot after expansion
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+    }
+
+    @Test
+    public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // We should have bubbles & their notifs should not be suppressed
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        mTestableLooper.processAllMessages();
+        assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+        // Expand
+        mBubbleController.expandStack();
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+        // Notif is suppressed after expansion
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        // Notif shouldn't show dot after expansion
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+        // Send update
+        mEntryListener.onEntryUpdated(mRow.getEntry());
+
+        // Nothing should have changed
+        // Notif is suppressed after expansion
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        // Notif shouldn't show dot after expansion
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+    }
+
+    @Test
+    public void testRemoveLastExpandedCollapses() {
+        // Mark it as a bubble and add it explicitly
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mEntryListener.onEntryAdded(mRow2.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow2.getEntry());
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
+
+        // Expand
+        BubbleStackView stackView = mBubbleController.getStackView();
+        mBubbleController.expandStack();
+
+        assertTrue(mBubbleController.isStackExpanded());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().getKey());
+
+        // Last added is the one that is expanded
+        assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow2.getEntry()));
+
+        // Dismiss currently expanded
+        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+                BubbleController.DISMISS_USER_GESTURE);
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
+
+        // Make sure first bubble is selected
+        assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().getKey());
+
+        // Dismiss that one
+        mBubbleController.removeBubble(stackView.getExpandedBubble().getEntry(),
+                BubbleController.DISMISS_USER_GESTURE);
+
+        // Make sure state changes and collapse happens
+        verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().getKey());
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
+        assertFalse(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void testAutoExpand_fails_noFlag() {
+        assertFalse(mBubbleController.isStackExpanded());
+        setMetadataFlags(mRow.getEntry(),
+                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
+
+        // Add the auto expand bubble
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // Expansion shouldn't change
+        verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
+                mRow.getEntry().getKey());
+        assertFalse(mBubbleController.isStackExpanded());
+
+        // # of bubbles should change
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+    }
+
+    @Test
+    public void testAutoExpand_succeeds_withFlag() {
+        setMetadataFlags(mRow.getEntry(),
+                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
+
+        // Add the auto expand bubble
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // Expansion should change
+        verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
+                mRow.getEntry().getKey());
+        assertTrue(mBubbleController.isStackExpanded());
+
+        // # of bubbles should change
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+    }
+
+    @Test
+    public void testSuppressNotif_onInitialNotif() {
+        setMetadataFlags(mRow.getEntry(),
+                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
+
+        // Add the suppress notif bubble
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // Notif should be suppressed because we were foreground
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        // Dot + flyout is hidden because notif is suppressed
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+
+        // # of bubbles should change
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+    }
+
+    @Test
+    public void testSuppressNotif_onUpdateNotif() {
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // Should not be suppressed
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        // Should show dot
+        assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+
+        // Update to suppress notif
+        setMetadataFlags(mRow.getEntry(),
+                Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        // Notif should be suppressed
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+        // Dot + flyout is hidden because notif is suppressed
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+        assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
+
+        // # of bubbles should change
+        verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
+    }
+
+    @Test
+    public void testMarkNewNotificationAsShowInShade() {
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        mTestableLooper.processAllMessages();
+        assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
+    }
+
+    @Test
+    public void testAddNotif_notBubble() {
+        mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+
+        verify(mBubbleStateChangeListener, never()).onHasBubblesChanged(anyBoolean());
+        assertThat(mBubbleController.hasBubbles()).isFalse();
+    }
+
+    @Test
+    public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.removeBubble(mRow.getEntry(), BubbleController.DISMISS_AGED);
+        verify(mDeleteIntent, never()).send();
+    }
+
+    @Test
+    public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.removeBubble(
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+        verify(mDeleteIntent, times(1)).send();
+    }
+
+    @Test
+    public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
+        mBubbleController.updateBubble(mRow.getEntry());
+        mBubbleController.updateBubble(mRow2.getEntry());
+        mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
+        verify(mDeleteIntent, times(2)).send();
+    }
+
+    @Test
+    public void testRemoveBubble_noLongerBubbleAfterUpdate()
+            throws PendingIntent.CanceledException {
+        mBubbleController.updateBubble(mRow.getEntry());
+        assertTrue(mBubbleController.hasBubbles());
+
+        mRow.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
+        mEntryListener.onEntryUpdated(mRow.getEntry());
+
+        assertFalse(mBubbleController.hasBubbles());
+        verify(mDeleteIntent, never()).send();
+    }
+
+    @Test
+    public void testRemoveBubble_entryListenerRemove() {
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+
+        // Removes the notification
+        mEntryListener.onEntryRemoved(mRow.getEntry(), 0);
+        assertFalse(mBubbleController.hasBubbles());
+    }
+
+    @Test
+    public void removeBubble_intercepted()  {
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+        // Intercept!
+        assertTrue(intercepted);
+        // Should update show in shade state
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(mRow.getEntry()));
+    }
+
+    @Test
+    public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
+        mEntryListener.onEntryAdded(mRow.getEntry());
+        mBubbleController.updateBubble(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        // Dismiss the bubble
+        mBubbleController.removeBubble(
+                mRow.getEntry(), BubbleController.DISMISS_USER_GESTURE);
+        assertFalse(mBubbleController.hasBubbles());
+
+        // Dismiss the notification
+        boolean intercepted = mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+        // It's no longer a bubble so we shouldn't intercept
+        assertFalse(intercepted);
+    }
+
+    @Test
+    public void testNotifyShadeSuppressionChange_notificationDismiss() {
+        BubbleController.NotificationSuppressionChangedListener listener =
+                mock(BubbleController.NotificationSuppressionChangedListener.class);
+        mBubbleData.setSuppressionChangedListener(listener);
+
+        mEntryListener.onEntryAdded(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        mBubbleController.handleDismissalInterception(mRow.getEntry());
+
+        // Should update show in shade state
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        // Should notify delegate that shade state changed
+        verify(listener).onBubbleNotificationSuppressionChange(
+                mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+    }
+
+    @Test
+    public void testNotifyShadeSuppressionChange_bubbleExpanded() {
+        BubbleController.NotificationSuppressionChangedListener listener =
+                mock(BubbleController.NotificationSuppressionChangedListener.class);
+        mBubbleData.setSuppressionChangedListener(listener);
+
+        mEntryListener.onEntryAdded(mRow.getEntry());
+
+        assertTrue(mBubbleController.hasBubbles());
+        assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        mBubbleData.setExpanded(true);
+
+        // Once a bubble is expanded the notif is suppressed
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                mRow.getEntry()));
+
+        // Should notify delegate that shade state changed
+        verify(listener).onBubbleNotificationSuppressionChange(
+                mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+    }
+
+    @Test
+    public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception {
+        // GIVEN a group summary with a bubble child
+        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onEntryAdded(groupedBubble.getEntry());
+        groupSummary.addChildNotification(groupedBubble);
+        assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+        // WHEN the summary is dismissed
+        mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+        // THEN the summary and bubbled child are suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getEntry()));
+        assertTrue(mBubbleData.isSummarySuppressed(groupSummary.getEntry().getSbn().getGroupKey()));
+    }
+
+    @Test
+    public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception {
+        // GIVEN a group summary with a bubble child
+        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0);
+        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onEntryAdded(groupedBubble.getEntry());
+        groupSummary.addChildNotification(groupedBubble);
+        assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+
+        // GIVEN the summary is dismissed
+        mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+        // WHEN the summary is cancelled by the app
+        mEntryListener.onEntryRemoved(groupSummary.getEntry(), 0);
+
+        // THEN the summary and its children are removed from bubble data
+        assertFalse(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+        assertFalse(mBubbleData.isSummarySuppressed(
+                groupSummary.getEntry().getSbn().getGroupKey()));
+    }
+
+    @Test
+    public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren()
+            throws Exception {
+        // GIVEN a group summary with two (non-bubble) children and one bubble child
+        ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2);
+        ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup();
+        mEntryListener.onEntryAdded(groupedBubble.getEntry());
+        groupSummary.addChildNotification(groupedBubble);
+
+        // WHEN the summary is dismissed
+        mBubbleController.handleDismissalInterception(groupSummary.getEntry());
+
+        // THEN only the NON-bubble children are dismissed
+        List<ExpandableNotificationRow> childrenRows = groupSummary.getNotificationChildren();
+        verify(mNotifCallback, times(1)).removeNotification(
+                childrenRows.get(0).getEntry(), REASON_GROUP_SUMMARY_CANCELED);
+        verify(mNotifCallback, times(1)).removeNotification(
+                childrenRows.get(1).getEntry(), REASON_GROUP_SUMMARY_CANCELED);
+        verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()), anyInt());
+
+        // THEN the bubble child still exists as a bubble and is suppressed from the shade
+        assertTrue(mBubbleData.hasBubbleWithKey(groupedBubble.getEntry().getKey()));
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupedBubble.getEntry()));
+
+        // THEN the summary is also suppressed from the shade
+        assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+                groupSummary.getEntry()));
+    }
+
+    static class TestableBubbleController extends BubbleController {
+        // Let's assume surfaces can be synchronized immediately.
+        TestableBubbleController(Context context,
+                NotificationShadeWindowController notificationShadeWindowController,
+                StatusBarStateController statusBarStateController,
+                ShadeController shadeController,
+                BubbleData data,
+                ConfigurationController configurationController,
+                NotificationInterruptionStateProvider interruptionStateProvider,
+                ZenModeController zenModeController,
+                NotificationLockscreenUserManager lockscreenUserManager,
+                NotificationGroupManager groupManager,
+                NotificationEntryManager entryManager,
+                NotifPipeline notifPipeline,
+                FeatureFlags featureFlags,
+                DumpController dumpController) {
+            super(context,
+                    notificationShadeWindowController, statusBarStateController, shadeController,
+                    data, Runnable::run, configurationController, interruptionStateProvider,
+                    zenModeController, lockscreenUserManager, groupManager, entryManager,
+                    notifPipeline, featureFlags, dumpController);
+            setInflateSynchronously(true);
+        }
+    }
+
+    static class TestableNotificationInterruptionStateProvider extends
+            NotificationInterruptionStateProvider {
+
+        TestableNotificationInterruptionStateProvider(Context context,
+                NotificationFilter filter, StatusBarStateController controller,
+                BatteryController batteryController) {
+            super(context, filter, controller, batteryController);
+            mUseHeadsUp = true;
+        }
+    }
+
+    /**
+     * Sets the bubble metadata flags for this entry. These flags are normally set by
+     * NotificationManagerService when the notification is sent, however, these tests do not
+     * go through that path so we set them explicitly when testing.
+     */
+    private void setMetadataFlags(NotificationEntry entry, int flag, boolean enableFlag) {
+        Notification.BubbleMetadata bubbleMetadata =
+                entry.getSbn().getNotification().getBubbleMetadata();
+        int flags = bubbleMetadata.getFlags();
+        if (enableFlag) {
+            flags |= flag;
+        } else {
+            flags &= ~flag;
+        }
+        bubbleMetadata.setFlags(flags);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
deleted file mode 100644
index 4a90bb9..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java
+++ /dev/null
@@ -1,69 +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.log;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.SysuiTestCase;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class RichEventTest extends SysuiTestCase {
-
-    private static final int TOTAL_EVENT_TYPES = 1;
-
-    @Test
-    public void testCreateRichEvent_invalidType() {
-        try {
-            // indexing for events starts at 0, so TOTAL_EVENT_TYPES is an invalid type
-            new TestableRichEvent(Event.DEBUG, TOTAL_EVENT_TYPES, "msg");
-        } catch (IllegalArgumentException e) {
-            // expected
-            return;
-        }
-
-        Assert.fail("Expected an invalidArgumentException since the event type was invalid.");
-    }
-
-    @Test
-    public void testCreateRichEvent() {
-        final int eventType = 0;
-        RichEvent e = new TestableRichEvent(Event.DEBUG, eventType, "msg");
-        assertEquals(e.getType(), eventType);
-    }
-
-    class TestableRichEvent extends RichEvent {
-        TestableRichEvent(int logLevel, int type, String reason) {
-            init(logLevel, type, reason);
-        }
-
-        @Override
-        public String[] getEventLabels() {
-            return new String[]{"ACTION_NAME"};
-        }
-    }
-
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
deleted file mode 100644
index e7b317e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java
+++ /dev/null
@@ -1,111 +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.log;
-
-import static junit.framework.Assert.assertEquals;
-
-import android.testing.AndroidTestingRunner;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.DumpController;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class SysuiLogTest extends SysuiTestCase {
-    private static final String TEST_ID = "TestLogger";
-    private static final String TEST_MSG = "msg";
-    private static final int MAX_LOGS = 5;
-
-    @Mock
-    private DumpController mDumpController;
-    private SysuiLog<Event> mSysuiLog;
-
-    @Before
-    public void setup() {
-        MockitoAnnotations.initMocks(this);
-    }
-
-    @Test
-    public void testLogDisabled_noLogsWritten() {
-        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, false);
-        assertEquals(null, mSysuiLog.mTimeline);
-
-        mSysuiLog.log(createEvent(TEST_MSG));
-        assertEquals(null, mSysuiLog.mTimeline);
-    }
-
-    @Test
-    public void testLogEnabled_logWritten() {
-        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
-        assertEquals(0, mSysuiLog.mTimeline.size());
-
-        mSysuiLog.log(createEvent(TEST_MSG));
-        assertEquals(1, mSysuiLog.mTimeline.size());
-    }
-
-    @Test
-    public void testMaxLogs() {
-        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
-        assertEquals(mSysuiLog.mTimeline.size(), 0);
-
-        for (int i = 0; i < MAX_LOGS + 1; i++) {
-            mSysuiLog.log(createEvent(TEST_MSG + i));
-        }
-
-        assertEquals(MAX_LOGS, mSysuiLog.mTimeline.size());
-
-        // check the first message (msg0) was replaced with msg1:
-        assertEquals(TEST_MSG + "1", mSysuiLog.mTimeline.getFirst().getMessage());
-    }
-
-    @Test
-    public void testRecycleLogs() {
-        // GIVEN a SysuiLog with one log
-        mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true);
-        Event e = createEvent(TEST_MSG); // msg
-        mSysuiLog.log(e); // Logs: [msg]
-
-        Event recycledEvent = null;
-        // WHEN we add MAX_LOGS after the first log
-        for (int i = 0; i < MAX_LOGS; i++) {
-            recycledEvent = mSysuiLog.log(createEvent(TEST_MSG + i));
-        }
-        // Logs: [msg1, msg2, msg3, msg4]
-
-        // THEN we see the recycledEvent is e
-        assertEquals(e, recycledEvent);
-    }
-
-    private Event createEvent(String msg) {
-        return new Event().init(msg);
-    }
-
-    public class TestSysuiLog extends SysuiLog<Event> {
-        protected TestSysuiLog(DumpController dumpController, String id, int maxLogs,
-                boolean enabled) {
-            super(dumpController, id, maxLogs, enabled, false);
-        }
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 4becd52..9fe2569 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -88,8 +88,8 @@
     }
 
     @Test
-    fun testBooleanTileHasBooleanState() {
-        `when`(mTileServiceManager.isBooleanTile).thenReturn(true)
+    fun testToggleableTileHasBooleanState() {
+        `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
         customTile = CustomTile.create(mTileHost, TILE_SPEC)
 
         assertTrue(customTile.state is QSTile.BooleanState)
@@ -104,7 +104,7 @@
 
     @Test
     fun testValueUpdatedInBooleanTile() {
-        `when`(mTileServiceManager.isBooleanTile).thenReturn(true)
+        `when`(mTileServiceManager.isToggleableTile).thenReturn(true)
         customTile = CustomTile.create(mTileHost, TILE_SPEC)
         customTile.qsTile.icon = mock(Icon::class.java)
         `when`(customTile.qsTile.icon.loadDrawable(any(Context::class.java)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 9e5e582..42fd288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -105,7 +105,7 @@
             defaultServiceInfo = new ServiceInfo();
             defaultServiceInfo.metaData = new Bundle();
             defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
-            defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_BOOLEAN_TILE, true);
+            defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true);
         }
         when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
                 .thenReturn(defaultServiceInfo);
@@ -244,7 +244,7 @@
     }
 
     @Test
-    public void testBooleanTile() throws Exception {
-        assertTrue(mStateManager.isBooleanTile());
+    public void testToggleableTile() throws Exception {
+        assertTrue(mStateManager.isToggleableTile());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 0a3bc6d..1d4b4be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -53,8 +53,8 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitor.BatteryStatus;
 import com.android.settingslib.Utils;
+import com.android.settingslib.fuelgauge.BatteryStatus;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dock.DockManager;
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 b51581f..07f6936 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
@@ -79,7 +79,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
@@ -139,7 +138,7 @@
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private RowInflaterTask mAsyncInflationTask;
-    @Mock private NotifLog mNotifLog;
+    @Mock private NotificationEntryManagerLogger mLogger;
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private LeakDetector mLeakDetector;
     @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
@@ -234,14 +233,14 @@
         when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
         mEntryManager = new TestableNotificationEntryManager(
-                mNotifLog,
+                mLogger,
                 mGroupManager,
                 new NotificationRankingManager(
                         () -> mock(NotificationMediaManager.class),
                         mGroupManager,
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
-                        mNotifLog,
+                        mLogger,
                         mock(NotificationSectionsFeatureManager.class),
                         mock(PeopleNotificationIdentifier.class),
                         mock(HighPriorityProvider.class)),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
index a9f9db6..0e730e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt
@@ -22,7 +22,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder
-import com.android.systemui.statusbar.notification.logging.NotifLog
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -34,7 +33,7 @@
  * Enable some test capabilities for NEM without making everything public on the base class
  */
 class TestableNotificationEntryManager(
-    log: NotifLog,
+    logger: NotificationEntryManagerLogger,
     gm: NotificationGroupManager,
     rm: NotificationRankingManager,
     ke: KeyguardEnvironment,
@@ -43,7 +42,7 @@
     notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>,
     leakDetector: LeakDetector,
     fgsFeatureController: ForegroundServiceDismissalFeatureController
-) : NotificationEntryManager(log, gm, rm, ke, ff, rb,
+) : NotificationEntryManager(logger, gm, rm, ke, ff, rb,
         notificationRemoteInputManagerLazy, leakDetector, fgsFeatureController) {
 
     public var countDownLatch: CountDownLatch = CountDownLatch(1)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 7c94ed2..abc0f3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -34,6 +34,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
@@ -47,6 +48,7 @@
 import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationStats;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
@@ -69,6 +71,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.util.Assert;
 
@@ -98,11 +101,19 @@
     @Spy private RecordingCollectionListener mCollectionListener;
     @Mock private CollectionReadyForBuildListener mBuildListener;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private DismissedByUserStats mDismissedByUserStats;
 
     @Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
     @Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
     @Spy private RecordingLifetimeExtender mExtender3 = new RecordingLifetimeExtender("Extender3");
 
+    @Spy private RecordingDismissInterceptor mInterceptor1 = new RecordingDismissInterceptor(
+            "Interceptor1");
+    @Spy private RecordingDismissInterceptor mInterceptor2 = new RecordingDismissInterceptor(
+            "Interceptor2");
+    @Spy private RecordingDismissInterceptor mInterceptor3 = new RecordingDismissInterceptor(
+            "Interceptor3");
+
     @Captor private ArgumentCaptor<BatchableNotificationHandler> mListenerCaptor;
     @Captor private ArgumentCaptor<NotificationEntry> mEntryCaptor;
     @Captor private ArgumentCaptor<Collection<NotificationEntry>> mBuildListCaptor;
@@ -441,6 +452,169 @@
         assertEquals(NOT_DISMISSED, entry3.getDismissState());
     }
 
+    @Test
+    public void testDismissInterceptorsAreCalled() throws RemoteException {
+        // GIVEN a collection with notifications with multiple dismiss interceptors
+        mInterceptor1.shouldInterceptDismissal = true;
+        mInterceptor2.shouldInterceptDismissal = true;
+        mInterceptor3.shouldInterceptDismissal = false;
+        mCollection.addNotificationDismissInterceptor(mInterceptor1);
+        mCollection.addNotificationDismissInterceptor(mInterceptor2);
+        mCollection.addNotificationDismissInterceptor(mInterceptor3);
+
+        NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        // WHEN a notification is manually dismissed
+        DismissedByUserStats stats = new DismissedByUserStats(
+                NotificationStats.DISMISSAL_SHADE,
+                NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+                NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+        mCollection.dismissNotification(entry, stats);
+
+        // THEN all interceptors get checked
+        verify(mInterceptor1).shouldInterceptDismissal(entry);
+        verify(mInterceptor2).shouldInterceptDismissal(entry);
+        verify(mInterceptor3).shouldInterceptDismissal(entry);
+        assertEquals(List.of(mInterceptor1, mInterceptor2), entry.mDismissInterceptors);
+
+        // THEN we never send the dismissal to system server
+        verify(mStatusBarService, never()).onNotificationClear(
+                notif.sbn.getPackageName(),
+                notif.sbn.getTag(),
+                47,
+                notif.sbn.getUser().getIdentifier(),
+                notif.sbn.getKey(),
+                stats.dismissalSurface,
+                stats.dismissalSentiment,
+                stats.notificationVisibility);
+    }
+
+    @Test
+    public void testDismissInterceptorsCanceledWhenNotifIsUpdated() throws RemoteException {
+        // GIVEN a few lifetime extenders and a couple notifications
+        mCollection.addNotificationDismissInterceptor(mInterceptor1);
+        mCollection.addNotificationDismissInterceptor(mInterceptor2);
+
+        mInterceptor1.shouldInterceptDismissal = true;
+        mInterceptor2.shouldInterceptDismissal = true;
+
+        NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        // WHEN a notification is manually dismissed and intercepted
+        DismissedByUserStats stats = new DismissedByUserStats(
+                NotificationStats.DISMISSAL_SHADE,
+                NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+                NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+        mCollection.dismissNotification(entry, stats);
+        assertEquals(List.of(mInterceptor1, mInterceptor2), entry.mDismissInterceptors);
+        clearInvocations(mInterceptor1, mInterceptor2);
+
+        // WHEN the notification is reposted
+        mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+
+        // THEN all of the active dismissal interceptors are canceled
+        verify(mInterceptor1).cancelDismissInterception(entry);
+        verify(mInterceptor2).cancelDismissInterception(entry);
+        assertEquals(List.of(), entry.mDismissInterceptors);
+
+        // THEN the notification is never sent to system server to dismiss
+        verify(mStatusBarService, never()).onNotificationClear(
+                eq(notif.sbn.getPackageName()),
+                eq(notif.sbn.getTag()),
+                eq(47),
+                eq(notif.sbn.getUser().getIdentifier()),
+                eq(notif.sbn.getKey()),
+                anyInt(),
+                anyInt(),
+                anyObject());
+    }
+
+    @Test
+    public void testEndingAllDismissInterceptorsSendsDismiss() throws RemoteException {
+        // GIVEN a collection with notifications a dismiss interceptor
+        mInterceptor1.shouldInterceptDismissal = true;
+        mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+        NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        // GIVEN a notification is manually dismissed
+        DismissedByUserStats stats = new DismissedByUserStats(
+                NotificationStats.DISMISSAL_SHADE,
+                NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+                NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+        mCollection.dismissNotification(entry, stats);
+
+        // WHEN all interceptors end their interception dismissal
+        mInterceptor1.shouldInterceptDismissal = false;
+        mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
+                mDismissedByUserStats);
+
+        // THEN we send the dismissal to system server
+        verify(mStatusBarService, times(1)).onNotificationClear(
+                eq(notif.sbn.getPackageName()),
+                eq(notif.sbn.getTag()),
+                eq(47),
+                eq(notif.sbn.getUser().getIdentifier()),
+                eq(notif.sbn.getKey()),
+                anyInt(),
+                anyInt(),
+                anyObject());
+    }
+
+    @Test
+    public void testEndDismissInterceptionUpdatesDismissInterceptors() throws RemoteException {
+        // GIVEN a collection with notifications with multiple dismiss interceptors
+        mInterceptor1.shouldInterceptDismissal = true;
+        mInterceptor2.shouldInterceptDismissal = true;
+        mInterceptor3.shouldInterceptDismissal = false;
+        mCollection.addNotificationDismissInterceptor(mInterceptor1);
+        mCollection.addNotificationDismissInterceptor(mInterceptor2);
+        mCollection.addNotificationDismissInterceptor(mInterceptor3);
+
+        NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        // GIVEN a notification is manually dismissed
+        DismissedByUserStats stats = new DismissedByUserStats(
+                NotificationStats.DISMISSAL_SHADE,
+                NotificationStats.DISMISS_SENTIMENT_NEUTRAL,
+                NotificationVisibility.obtain(entry.getKey(), 7, 2, true));
+        mCollection.dismissNotification(entry, stats);
+
+       // WHEN an interceptor ends its interception
+        mInterceptor1.shouldInterceptDismissal = false;
+        mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
+                mDismissedByUserStats);
+
+        // THEN all interceptors get checked
+        verify(mInterceptor1).shouldInterceptDismissal(entry);
+        verify(mInterceptor2).shouldInterceptDismissal(entry);
+        verify(mInterceptor3).shouldInterceptDismissal(entry);
+
+        // THEN mInterceptor2 is the only dismiss interceptor
+        assertEquals(List.of(mInterceptor2), entry.mDismissInterceptors);
+    }
+
+
+    @Test(expected = IllegalStateException.class)
+    public void testEndingDismissalOfNonInterceptedThrows() throws RemoteException {
+        // GIVEN a collection with notifications with a dismiss interceptor that hasn't been called
+        mInterceptor1.shouldInterceptDismissal = false;
+        mCollection.addNotificationDismissInterceptor(mInterceptor1);
+
+        NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotificationEntry entry = mCollectionListener.getEntry(notif.key);
+
+        // WHEN we try to end the dismissal of an interceptor that didn't intercept the notif
+        mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry,
+                mDismissedByUserStats);
+
+        // THEN an exception is thrown
+    }
+
     @Test(expected = IllegalStateException.class)
     public void testDismissingNonExistentNotificationThrows() {
         // GIVEN a collection that originally had three notifs, but where one was dismissed
@@ -894,6 +1068,36 @@
         }
     }
 
+    private static class RecordingDismissInterceptor implements NotifDismissInterceptor {
+        private final String mName;
+
+        public @Nullable OnEndDismissInterception onEndInterceptionCallback;
+        public boolean shouldInterceptDismissal = false;
+
+        private RecordingDismissInterceptor(String name) {
+            mName = name;
+        }
+
+        @Override
+        public String getName() {
+            return mName;
+        }
+
+        @Override
+        public void setCallback(OnEndDismissInterception callback) {
+            this.onEndInterceptionCallback = callback;
+        }
+
+        @Override
+        public boolean shouldInterceptDismissal(NotificationEntry entry) {
+            return shouldInterceptDismissal;
+        }
+
+        @Override
+        public void cancelDismissInterception(NotificationEntry entry) {
+        }
+    }
+
     private static final String TEST_PACKAGE = "com.android.test.collection";
     private static final String TEST_PACKAGE2 = "com.android.test.collection2";
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 7ab4846..c6b496d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -27,10 +27,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking
 import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger
 import com.android.systemui.statusbar.notification.NotificationFilter
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
-import com.android.systemui.statusbar.notification.logging.NotifLog
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
@@ -62,7 +62,7 @@
                 mock(NotificationGroupManager::class.java),
                 mock(HeadsUpManager::class.java),
                 mock(NotificationFilter::class.java),
-                mock(NotifLog::class.java),
+                mock(NotificationEntryManagerLogger::class.java),
                 mock(NotificationSectionsFeatureManager::class.java),
                 personNotificationIdentifier,
                 HighPriorityProvider(personNotificationIdentifier)
@@ -189,7 +189,7 @@
         groupManager: NotificationGroupManager,
         headsUpManager: HeadsUpManager,
         filter: NotificationFilter,
-        notifLog: NotifLog,
+        logger: NotificationEntryManagerLogger,
         sectionsFeatureManager: NotificationSectionsFeatureManager,
         peopleNotificationIdentifier: PeopleNotificationIdentifier,
         highPriorityProvider: HighPriorityProvider
@@ -198,7 +198,7 @@
         groupManager,
         headsUpManager,
         filter,
-        notifLog,
+        logger,
         sectionsFeatureManager,
         peopleNotificationIdentifier,
         highPriorityProvider
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 3d9832d..9b2e0c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -199,9 +199,17 @@
     /**
      * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
      */
+    public ExpandableNotificationRow createBubbleInGroup()
+            throws Exception {
+        return createBubble(makeBubbleMetadata(null), PKG, true);
+    }
+
+    /**
+     * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble.
+     */
     public ExpandableNotificationRow createBubble()
             throws Exception {
-        return createBubble(makeBubbleMetadata(null), PKG);
+        return createBubble(makeBubbleMetadata(null), PKG, false);
     }
 
     /**
@@ -211,7 +219,7 @@
      */
     public ExpandableNotificationRow createBubble(@Nullable PendingIntent deleteIntent)
             throws Exception {
-        return createBubble(makeBubbleMetadata(deleteIntent), PKG);
+        return createBubble(makeBubbleMetadata(deleteIntent), PKG, false);
     }
 
     /**
@@ -221,8 +229,14 @@
      */
     public ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg)
             throws Exception {
+        return createBubble(bubbleMetadata, pkg, false);
+    }
+
+    private ExpandableNotificationRow createBubble(BubbleMetadata bubbleMetadata, String pkg,
+            boolean inGroup)
+            throws Exception {
         Notification n = createNotification(false /* isGroupSummary */,
-                null /* groupKey */, bubbleMetadata);
+                inGroup ? GROUP_KEY : null /* groupKey */, bubbleMetadata);
         n.flags |= FLAG_BUBBLE;
         ExpandableNotificationRow row = generateRow(n, pkg, UID, USER_HANDLE,
                 0 /* extraInflationFlags */, IMPORTANCE_HIGH);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 66aa5e1..775f722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -67,7 +67,7 @@
     }
 
     @Test
-    public void testSetShouldContentViewsBeBound_bindsContent() {
+    public void testRequireContentViews() {
         // WHEN inflation flags are set and pipeline is invalidated.
         final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
         RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
@@ -85,7 +85,7 @@
     }
 
     @Test
-    public void testSetShouldContentViewsBeBound_unbindsContent() {
+    public void testFreeContentViews() {
         // GIVEN a view with all content bound.
         RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
         params.requireContentViews(FLAG_CONTENT_VIEW_ALL);
@@ -100,6 +100,28 @@
     }
 
     @Test
+    public void testRebindAllContentViews() {
+        // GIVEN a view with content bound.
+        RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
+        final int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
+        params.requireContentViews(flags);
+        params.clearDirtyContentViews();
+
+        // WHEN we request rebind and stage executed.
+        params.rebindAllContentViews();
+        mRowContentBindStage.executeStage(mEntry, mRow, (en) -> { });
+
+        // THEN binder binds inflation flags.
+        verify(mBinder).bindContent(
+                eq(mEntry),
+                any(),
+                eq(flags),
+                any(),
+                anyBoolean(),
+                any());
+    }
+
+    @Test
     public void testSetUseLowPriority() {
         // GIVEN a view with all content bound.
         RowContentBindParams params = mRowContentBindStage.getStageParams(mEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 70d76f0..b16e52c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -65,6 +65,7 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
 import com.android.systemui.statusbar.notification.NotificationFilter;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.TestableNotificationEntryManager;
@@ -74,7 +75,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
@@ -163,14 +163,14 @@
         ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor
                 .forClass(UserChangedListener.class);
         mEntryManager = new TestableNotificationEntryManager(
-                mock(NotifLog.class),
+                mock(NotificationEntryManagerLogger.class),
                 mock(NotificationGroupManager.class),
                 new NotificationRankingManager(
                         () -> mock(NotificationMediaManager.class),
                         mGroupManager,
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
-                        mock(NotifLog.class),
+                        mock(NotificationEntryManagerLogger.class),
                         mock(NotificationSectionsFeatureManager.class),
                         mock(PeopleNotificationIdentifier.class),
                         mock(HighPriorityProvider.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 631c580..cd91f22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -24,10 +26,12 @@
 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 android.net.ConnectivityManager;
+import android.net.TetheringManager;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
+import android.os.UserManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
@@ -38,11 +42,14 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.concurrent.Executor;
 
 @SmallTest
@@ -51,13 +58,19 @@
 public class HotspotControllerImplTest extends SysuiTestCase {
 
     @Mock
-    private ConnectivityManager mConnectivityManager;
+    private TetheringManager mTetheringManager;
     @Mock
     private WifiManager mWifiManager;
     @Mock
+    private UserManager mUserManager;
+    @Mock
     private HotspotController.Callback mCallback1;
     @Mock
     private HotspotController.Callback mCallback2;
+    @Mock
+    private TetheringManager.TetheringInterfaceRegexps mTetheringInterfaceRegexps;
+    @Captor
+    private ArgumentCaptor<TetheringManager.TetheringEventCallback> mTetheringCallbackCaptor;
     private HotspotControllerImpl mController;
     private TestableLooper mLooper;
 
@@ -66,8 +79,13 @@
         MockitoAnnotations.initMocks(this);
         mLooper = TestableLooper.get(this);
 
-        mContext.addMockSystemService(ConnectivityManager.class, mConnectivityManager);
         mContext.addMockSystemService(WifiManager.class, mWifiManager);
+        mContext.addMockSystemService(TetheringManager.class, mTetheringManager);
+        mContext.addMockSystemService(UserManager.class, mUserManager);
+
+        when(mUserManager.isUserAdmin(anyInt())).thenReturn(true);
+        when(mTetheringInterfaceRegexps.getTetherableWifiRegexs()).thenReturn(
+                Collections.singletonList("test"));
 
         doAnswer((InvocationOnMock invocation) -> {
             ((WifiManager.SoftApCallback) invocation.getArgument(1))
@@ -76,7 +94,11 @@
         }).when(mWifiManager).registerSoftApCallback(any(Executor.class),
                 any(WifiManager.SoftApCallback.class));
 
-        mController = new HotspotControllerImpl(mContext, new Handler(mLooper.getLooper()));
+        Handler handler = new Handler(mLooper.getLooper());
+
+        mController = new HotspotControllerImpl(mContext, handler, handler);
+        verify(mTetheringManager)
+                .registerTetheringEventCallback(any(), mTetheringCallbackCaptor.capture());
     }
 
     @Test
@@ -117,4 +139,28 @@
         verify(mWifiManager, never()).unregisterSoftApCallback(any());
     }
 
+    @Test
+    public void testDefault_hotspotNotSupported() {
+        assertFalse(mController.isHotspotSupported());
+    }
+
+    @Test
+    public void testHotspotSupported_rightConditions() {
+        mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+        mTetheringCallbackCaptor.getValue()
+                .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
+
+        assertTrue(mController.isHotspotSupported());
+    }
+
+    @Test
+    public void testHotspotSupported_callbackCalledOnChange() {
+        mController.addCallback(mCallback1);
+        mTetheringCallbackCaptor.getValue().onTetheringSupported(true);
+        mTetheringCallbackCaptor.getValue()
+                .onTetherableInterfaceRegexpsChanged(mTetheringInterfaceRegexps);
+
+        verify(mCallback1).onHotspotAvailabilityChanged(true);
+    }
+
 }
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index e0adb34d..8c4f733 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -59,16 +59,33 @@
     ],
 
     hostdex: true, // for hiddenapi check
-    visibility: [
-        "//frameworks/base/packages/Tethering:__subpackages__",
-        //TODO(b/147200698) remove below lines when the platform is built with stubs
-        "//frameworks/base",
-        "//frameworks/base/services",
-        "//frameworks/base/services/core",
-    ],
+    visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
     apex_available: ["com.android.tethering"],
 }
 
+droidstubs {
+    name: "framework-tethering-stubs-sources",
+    defaults: ["framework-module-stubs-defaults-module_libs_api"],
+    srcs: [
+        "src/android/net/TetheredClient.java",
+        "src/android/net/TetheringManager.java",
+        "src/android/net/TetheringConstants.java",
+    ],
+    libs: [
+        "tethering-aidl-interfaces-java",
+        "framework-all",
+    ],
+    sdk_version: "core_platform",
+}
+
+java_library {
+    name: "framework-tethering-stubs",
+    srcs: [":framework-tethering-stubs-sources"],
+    libs: ["framework-all"],
+    static_libs: ["tethering-aidl-interfaces-java"],
+    sdk_version: "core_platform",
+}
+
 filegroup {
     name: "framework-tethering-srcs",
     srcs: [
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 6514688..ca5ef09 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -34,6 +36,7 @@
  * @hide
  */
 @SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
 @TestApi
 public final class TetheredClient implements Parcelable {
     @NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index 00cf98e..df87ac9 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -16,6 +16,9 @@
 
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
 import android.os.ResultReceiver;
 
 /**
@@ -28,39 +31,30 @@
  * symbols from framework-tethering even when they are in a non-hidden class.
  * @hide
  */
+@SystemApi(client = MODULE_LIBRARIES)
 public class TetheringConstants {
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering to
      * enable if any.
-     *
-     * {@hide}
      */
     public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     /**
      * Extra used for communicating with the TetherService. Includes the type of tethering for
      * which to cancel provisioning.
-     *
-     * {@hide}
      */
     public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
     /**
      * Extra used for communicating with the TetherService. True to schedule a recheck of tether
      * provisioning.
-     *
-     * {@hide}
      */
     public static final String EXTRA_SET_ALARM = "extraSetAlarm";
     /**
      * Tells the TetherService to run a provision check now.
-     *
-     * {@hide}
      */
     public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
     /**
      * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
      * which will receive provisioning results. Can be left empty.
-     *
-     * {@hide}
      */
     public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
 }
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 53a358f..6a9f010 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,8 @@
  */
 package android.net;
 
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -50,6 +52,7 @@
  * @hide
  */
 @SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
 @TestApi
 public class TetheringManager {
     private static final String TAG = TetheringManager.class.getSimpleName();
@@ -177,6 +180,7 @@
      *                          service is not connected.
      * {@hide}
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public TetheringManager(@NonNull final Context context,
             @NonNull Supplier<IBinder> connectorSupplier) {
         mContext = context;
@@ -395,6 +399,7 @@
      * {@hide}
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public int tether(@NonNull final String iface) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "tether caller:" + callerPkg);
@@ -418,6 +423,7 @@
      * {@hide}
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public int untether(@NonNull final String iface) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "untether caller:" + callerPkg);
@@ -444,6 +450,7 @@
      * {@hide}
      */
     @Deprecated
+    @SystemApi(client = MODULE_LIBRARIES)
     public int setUsbTethering(final boolean enable) {
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "setUsbTethering caller:" + callerPkg);
@@ -702,6 +709,7 @@
      * {@hide}
      */
     // TODO: improve the usage of ResultReceiver, b/145096122
+    @SystemApi(client = MODULE_LIBRARIES)
     public void requestLatestTetheringEntitlementResult(final int type,
             @NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
         final String callerPkg = mContext.getOpPackageName();
@@ -982,6 +990,7 @@
      *               interface
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public int getLastTetherError(@NonNull final String iface) {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
@@ -1004,6 +1013,7 @@
      *        what interfaces are considered tetherable usb interfaces.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableUsbRegexs() {
         mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableUsbRegexs;
@@ -1018,6 +1028,7 @@
      *        what interfaces are considered tetherable wifi interfaces.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableWifiRegexs() {
         mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableWifiRegexs;
@@ -1032,6 +1043,7 @@
      *        what interfaces are considered tetherable bluetooth interfaces.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableBluetoothRegexs() {
         mCallback.waitForStarted();
         return mTetheringConfiguration.tetherableBluetoothRegexs;
@@ -1044,6 +1056,7 @@
      * @return an array of 0 or more Strings of tetherable interface names.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetherableIfaces() {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
@@ -1057,6 +1070,7 @@
      * @return an array of 0 or more String of currently tethered interface names.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetheredIfaces() {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
@@ -1076,6 +1090,7 @@
      *        which failed to tether.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public @NonNull String[] getTetheringErroredIfaces() {
         mCallback.waitForStarted();
         if (mTetherStatesParcel == null) return new String[0];
@@ -1103,6 +1118,7 @@
      * @return a boolean - {@code true} indicating Tethering is supported.
      * @hide
      */
+    @SystemApi(client = MODULE_LIBRARIES)
     public boolean isTetheringSupported() {
         final String callerPkg = mContext.getOpPackageName();
 
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 39c402b..64c16e4 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -1944,7 +1944,8 @@
             parcel.tetheringSupported = mDeps.isTetheringSupported();
             parcel.upstreamNetwork = mTetherUpstream;
             parcel.config = mConfig.toStableParcelable();
-            parcel.states = mTetherStatesParcel;
+            parcel.states =
+                    mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
             try {
                 callback.onCallbackStarted(parcel);
             } catch (RemoteException e) {
@@ -1953,6 +1954,17 @@
         });
     }
 
+    private TetherStatesParcel emptyTetherStatesParcel() {
+        final TetherStatesParcel parcel = new TetherStatesParcel();
+        parcel.availableList = new String[0];
+        parcel.tetheredList = new String[0];
+        parcel.localOnlyList = new String[0];
+        parcel.erroredIfaceList = new String[0];
+        parcel.lastErrorList = new int[0];
+
+        return parcel;
+    }
+
     /** Unregister tethering event callback */
     void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
         mHandler.post(() -> {
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 13174c5..c6905ec 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -34,7 +34,15 @@
         "TetheringApiCurrentLib",
         "testables",
     ],
+    // TODO(b/147200698) change sdk_version to module-current and
+    // remove framework-minus-apex, ext, and framework-res
+    sdk_version: "core_platform",
     libs: [
+        "framework-minus-apex",
+        "ext",
+        "framework-res",
+        "framework-wifi-stubs",
+        "framework-telephony-stubs",
         "android.test.runner",
         "android.test.base",
         "android.test.mock",
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 4710287..6d49e20 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
@@ -127,6 +127,7 @@
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.networkstack.tethering.R;
+import com.android.testutils.MiscAssertsKt;
 
 import org.junit.After;
 import org.junit.Before;
@@ -1220,6 +1221,16 @@
         }
     }
 
+    private void assertTetherStatesNotNullButEmpty(final TetherStatesParcel parcel) {
+        assertFalse(parcel == null);
+        assertEquals(0, parcel.availableList.length);
+        assertEquals(0, parcel.tetheredList.length);
+        assertEquals(0, parcel.localOnlyList.length);
+        assertEquals(0, parcel.erroredIfaceList.length);
+        assertEquals(0, parcel.lastErrorList.length);
+        MiscAssertsKt.assertFieldCountEquals(5, TetherStatesParcel.class);
+    }
+
     @Test
     public void testRegisterTetheringEventCallback() throws Exception {
         TestTetheringEventCallback callback = new TestTetheringEventCallback();
@@ -1232,7 +1243,7 @@
         callback.expectConfigurationChanged(
                 mTethering.getTetheringConfiguration().toStableParcelable());
         TetherStatesParcel tetherState = callback.pollTetherStatesChanged();
-        assertEquals(tetherState, null);
+        assertTetherStatesNotNullButEmpty(tetherState);
         // 2. Enable wifi tethering.
         UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
         when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState);
diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk
index fc7709c..dcdb80b 100644
--- a/packages/overlays/Android.mk
+++ b/packages/overlays/Android.mk
@@ -28,6 +28,7 @@
 	DisplayCutoutEmulationDoubleOverlay \
         DisplayCutoutEmulationHoleOverlay \
 	DisplayCutoutEmulationTallOverlay \
+	DisplayCutoutEmulationWaterfallOverlay \
 	FontNotoSerifSourceOverlay \
 	IconPackCircularAndroidOverlay \
 	IconPackCircularLauncherOverlay \
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk
new file mode 100644
index 0000000..b6b6dd1
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk
@@ -0,0 +1,14 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_RRO_THEME := DisplayCutoutEmulationWaterfall
+
+
+LOCAL_PRODUCT_MODULE := true
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationWaterfallOverlay
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..2d5bb14
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.internal.display.cutout.emulation.waterfall"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android"
+        android:category="com.android.internal.display_cutout_emulation"
+        android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..df2f3d1
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">48dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">176dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
new file mode 100644
index 0000000..6f692c8
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml
@@ -0,0 +1,35 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Height of the status bar in portrait. The height should be
+         Max((status bar content height + waterfall top size), top cutout size) -->
+    <dimen name="status_bar_height_portrait">28dp</dimen>
+    <!-- Max((28 + 20), 0) = 48 -->
+    <dimen name="status_bar_height_landscape">48dp</dimen>
+    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+
+    <dimen name="waterfall_display_left_edge_size">20dp</dimen>
+    <dimen name="waterfall_display_top_edge_size">0dp</dimen>
+    <dimen name="waterfall_display_right_edge_size">20dp</dimen>
+    <dimen name="waterfall_display_bottom_edge_size">0dp</dimen>
+</resources>
+
+
diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml
new file mode 100644
index 0000000..ed073d0
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="display_cutout_emulation_overlay">Waterfall cutout</string>
+
+</resources>
\ No newline at end of file
diff --git a/services/Android.bp b/services/Android.bp
index 28c8aee..32394f4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -75,7 +75,7 @@
 
     libs: [
         "android.hidl.manager-V1.0-java",
-        "framework-tethering"
+        "framework-tethering-stubs",
     ],
 
     plugins: [
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index a5877cc..565ee63 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -16,6 +16,8 @@
 
 package com.android.server.accessibility;
 
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER;
 import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
@@ -36,10 +38,11 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
-import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.HardwareBuffer;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Binder;
@@ -50,6 +53,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -71,6 +75,7 @@
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -106,6 +111,8 @@
     private final PowerManager mPowerManager;
     private final IPlatformCompat mIPlatformCompat;
 
+    private final Handler mMainHandler;
+
     // Handler for scheduling method invocations on the main thread.
     public final InvocationHandler mInvocationHandler;
 
@@ -238,6 +245,7 @@
         mSecurityPolicy = securityPolicy;
         mSystemActionPerformer = systemActionPerfomer;
         mSystemSupport = systemSupport;
+        mMainHandler = mainHandler;
         mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
         mA11yWindowManager = a11yWindowManager;
         mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
@@ -959,52 +967,72 @@
         mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled);
     }
 
-    @Nullable
     @Override
-    public Bitmap takeScreenshot(int displayId) {
+    public void takeScreenshot(int displayId, RemoteCallback callback) {
         synchronized (mLock) {
             if (!hasRightsToCurrentUserLocked()) {
-                return null;
+                sendScreenshotResult(true, null, callback);
+                return;
             }
 
             if (!mSecurityPolicy.canTakeScreenshotLocked(this)) {
-                return null;
+                sendScreenshotResult(true, null, callback);
+                throw new SecurityException("Services don't have the capability of taking"
+                        + " the screenshot.");
             }
         }
 
         if (!mSecurityPolicy.checkAccessibilityAccess(this)) {
-            return null;
+            sendScreenshotResult(true, null, callback);
+            return;
         }
 
         final Display display = DisplayManagerGlobal.getInstance()
                 .getRealDisplay(displayId);
         if (display == null) {
-            return null;
+            sendScreenshotResult(true, null, callback);
+            return;
         }
-        final Point displaySize = new Point();
-        display.getRealSize(displaySize);
 
-        final int rotation = display.getRotation();
-        Bitmap screenShot = null;
+        sendScreenshotResult(false, display, callback);
+    }
 
+    private void sendScreenshotResult(boolean noResult, Display display, RemoteCallback callback) {
+        final boolean noScreenshot = noResult;
         final long identity = Binder.clearCallingIdentity();
         try {
-            final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
-            // TODO (b/145893483): calling new API with the display as a parameter
-            // when surface control supported.
-            screenShot = SurfaceControl.screenshot(crop, displaySize.x, displaySize.y,
-                    rotation);
-            if (screenShot != null) {
-                // Optimization for telling the bitmap that all of the pixels are known to be
-                // opaque (false). This is meant as a drawing hint, as in some cases a bitmap
-                // that is known to be opaque can take a faster drawing case than one that may
-                // have non-opaque per-pixel alpha values.
-                screenShot.setHasAlpha(false);
-            }
+            mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
+                if (noScreenshot) {
+                    callback.sendResult(null);
+                    return;
+                }
+                final Point displaySize = new Point();
+                // TODO (b/145893483): calling new API with the display as a parameter
+                // when surface control supported.
+                final IBinder token = SurfaceControl.getInternalDisplayToken();
+                final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y);
+                final int rotation = display.getRotation();
+                display.getRealSize(displaySize);
+
+                final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
+                        SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, crop,
+                                displaySize.x, displaySize.y, false,
+                                rotation);
+                final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
+                final HardwareBuffer hardwareBuffer =
+                        HardwareBuffer.createFromGraphicBuffer(graphicBuffer);
+                final int colorSpaceId = screenshotBuffer.getColorSpace().getId();
+
+                // Send back the result.
+                final Bundle payload = new Bundle();
+                payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
+                        hardwareBuffer);
+                payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID, colorSpaceId);
+                callback.sendResult(payload);
+            }, null).recycleOnUse());
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
-        return screenShot;
     }
 
     @Override
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 25911a7..edb4445 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -16,8 +16,6 @@
 
 package com.android.server.accessibility;
 
-import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT;
-
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
 import android.Manifest;
@@ -27,20 +25,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
-import android.graphics.Bitmap;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
-import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 import android.view.Display;
 
-import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
@@ -393,15 +387,4 @@
             }
         }
     }
-
-    @Override
-    public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {
-        mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> {
-            final Bitmap screenshot = super.takeScreenshot(displayId);
-            // Send back the result.
-            final Bundle payload = new Bundle();
-            payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT, screenshot);
-            callback.sendResult(payload);
-        }, null).recycleOnUse());
-    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 5d9af26..d1c3a02 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -328,6 +328,6 @@
         public void onFingerprintGesture(int gesture) {}
 
         @Override
-        public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
+        public void takeScreenshot(int displayId, RemoteCallback callback) {}
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1cb9313..4474f60 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -82,6 +82,7 @@
 import com.android.server.autofill.AutofillManagerService.AutofillCompatState;
 import com.android.server.autofill.RemoteAugmentedAutofillService.RemoteAugmentedAutofillServiceCallbacks;
 import com.android.server.autofill.ui.AutoFillUI;
+import com.android.server.contentcapture.ContentCaptureManagerInternal;
 import com.android.server.infra.AbstractPerUserSystemService;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 
@@ -180,6 +181,8 @@
 
     private final InputMethodManagerInternal mInputMethodManagerInternal;
 
+    private final ContentCaptureManagerInternal mContentCaptureManagerInternal;
+
     AutofillManagerServiceImpl(AutofillManagerService master, Object lock,
             LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
             AutofillCompatState autofillCompatState,
@@ -192,10 +195,22 @@
         mFieldClassificationStrategy = new FieldClassificationStrategy(getContext(), userId);
         mAutofillCompatState = autofillCompatState;
         mInputMethodManagerInternal = LocalServices.getService(InputMethodManagerInternal.class);
+        mContentCaptureManagerInternal = LocalServices.getService(
+                ContentCaptureManagerInternal.class);
 
         updateLocked(disabled);
     }
 
+    boolean sendActivityAssistDataToContentCapture(@NonNull IBinder activityToken,
+            @NonNull Bundle data) {
+        if (mContentCaptureManagerInternal != null) {
+            mContentCaptureManagerInternal.sendActivityAssistData(getUserId(), activityToken, data);
+            return true;
+        }
+
+        return false;
+    }
+
     @GuardedBy("mLock")
     void onBackKeyPressed() {
         final RemoteAugmentedAutofillService remoteService =
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
new file mode 100644
index 0000000..1fc48d2
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionSession.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill;
+
+import static com.android.server.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.autofill.AutofillId;
+import android.view.inputmethod.InlineSuggestionsRequest;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+import com.android.server.inputmethod.InputMethodManagerInternal;
+
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Maintains an inline suggestion autofill session.
+ *
+ * <p> This class is thread safe.
+ */
+final class InlineSuggestionSession {
+
+    private static final String TAG = "InlineSuggestionSession";
+    private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
+
+    @NonNull
+    private final InputMethodManagerInternal mInputMethodManagerInternal;
+    private final int mUserId;
+    @NonNull
+    private final ComponentName mComponentName;
+    @NonNull
+    private final Object mLock;
+
+    @GuardedBy("mLock")
+    @Nullable
+    private CompletableFuture<ImeResponse> mPendingImeResponse;
+
+    InlineSuggestionSession(InputMethodManagerInternal inputMethodManagerInternal,
+            int userId, ComponentName componentName) {
+        mInputMethodManagerInternal = inputMethodManagerInternal;
+        mUserId = userId;
+        mComponentName = componentName;
+        mLock = new Object();
+    }
+
+    public void createRequest(@NonNull AutofillId currentViewId) {
+        synchronized (mLock) {
+            cancelCurrentRequest();
+            mPendingImeResponse = new CompletableFuture<>();
+            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
+                    mUserId, mComponentName, currentViewId,
+                    new InlineSuggestionsRequestCallbackImpl(mPendingImeResponse));
+        }
+    }
+
+    @Nullable
+    public ImeResponse waitAndGetImeResponse() {
+        CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
+        if (pendingImeResponse == null || pendingImeResponse.isCancelled()) {
+            return null;
+        }
+        try {
+            return pendingImeResponse.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
+        } catch (CancellationException e) {
+            Log.w(TAG, "Inline suggestions request cancelled");
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+        return null;
+    }
+
+    private void cancelCurrentRequest() {
+        CompletableFuture<ImeResponse> pendingImeResponse = getPendingImeResponse();
+        if (pendingImeResponse != null) {
+            pendingImeResponse.cancel(true);
+        }
+    }
+
+    @Nullable
+    @GuardedBy("mLock")
+    private CompletableFuture<ImeResponse> getPendingImeResponse() {
+        synchronized (mLock) {
+            return mPendingImeResponse;
+        }
+    }
+
+    private static final class InlineSuggestionsRequestCallbackImpl
+            extends IInlineSuggestionsRequestCallback.Stub {
+
+        private final CompletableFuture<ImeResponse> mResponse;
+
+        private InlineSuggestionsRequestCallbackImpl(CompletableFuture<ImeResponse> response) {
+            mResponse = response;
+        }
+
+        @Override
+        public void onInlineSuggestionsUnsupported() throws RemoteException {
+            if (sDebug) {
+                Log.d(TAG, "onInlineSuggestionsUnsupported() called.");
+            }
+            mResponse.cancel(true);
+        }
+
+        @Override
+        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
+                IInlineSuggestionsResponseCallback callback) throws RemoteException {
+            if (sDebug) {
+                Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+            }
+            if (request != null && callback != null) {
+                mResponse.complete(new ImeResponse(request, callback));
+            } else {
+                mResponse.cancel(true);
+            }
+        }
+    }
+
+    /**
+     * A data class wrapping IME responses for the inline suggestion request.
+     */
+    public static class ImeResponse {
+        @NonNull
+        private final InlineSuggestionsRequest mRequest;
+
+        @NonNull
+        private final IInlineSuggestionsResponseCallback mCallback;
+
+        ImeResponse(@NonNull InlineSuggestionsRequest request,
+                @NonNull IInlineSuggestionsResponseCallback callback) {
+            mRequest = request;
+            mCallback = callback;
+        }
+
+        public InlineSuggestionsRequest getRequest() {
+            return mRequest;
+        }
+
+        public IInlineSuggestionsResponseCallback getCallback() {
+            return mCallback;
+        }
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 880c401..d93ac68 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -166,8 +166,8 @@
                                 public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
                                     mCallbacks.resetLastResponse();
                                     maybeRequestShowInlineSuggestions(sessionId,
-                                            inlineSuggestionsData, focusedId,
-                                            inlineSuggestionsCallback, client,
+                                            inlineSuggestionsRequest, inlineSuggestionsData,
+                                            focusedId, inlineSuggestionsCallback, client,
                                             onErrorCallback);
                                     requestAutofill.complete(null);
                                 }
@@ -231,10 +231,12 @@
     }
 
     private void maybeRequestShowInlineSuggestions(int sessionId,
-            @Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
+            @Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
+            @NonNull AutofillId focusedId,
             @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
             @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
-        if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
+        if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
+                || request == null) {
             return;
         }
         mCallbacks.setLastResponse(sessionId);
@@ -242,7 +244,7 @@
         try {
             inlineSuggestionsCallback.onInlineSuggestionsResponse(
                     InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
-                            inlineSuggestionsData, focusedId, mContext,
+                            request, inlineSuggestionsData, focusedId, mContext,
                             dataset -> {
                                 mCallbacks.logAugmentedAutofillSelected(sessionId,
                                         dataset.getId());
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 7e5123c..53f85ea 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -100,7 +100,6 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.view.IInlineSuggestionsRequestCallback;
 import com.android.internal.view.IInlineSuggestionsResponseCallback;
 import com.android.server.autofill.ui.AutoFillUI;
 import com.android.server.autofill.ui.InlineSuggestionFactory;
@@ -114,11 +113,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -159,9 +153,6 @@
     /** 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;
 
@@ -310,11 +301,8 @@
     @GuardedBy("mLock")
     private boolean mForAugmentedAutofillOnly;
 
-    @NonNull
-    private final InputMethodManagerInternal mInputMethodManagerInternal;
-
     @Nullable
-    private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback;
+    private final InlineSuggestionSession mInlineSuggestionSession;
 
     /**
      * Receiver of assist data from the app's {@link Activity}.
@@ -415,14 +403,16 @@
                 final ArrayList<FillContext> contexts =
                         mergePreviousSessionLocked(/* forSave= */ false);
 
-                final InlineSuggestionsRequest suggestionsRequest =
-                        mInlineSuggestionsRequestCallback != null
-                                ? mInlineSuggestionsRequestCallback.getRequest() : null;
+                final InlineSuggestionSession.ImeResponse imeResponse =
+                        mInlineSuggestionSession.waitAndGetImeResponse();
 
                 request = new FillRequest(requestId, contexts, mClientState, flags,
-                        suggestionsRequest);
+                        imeResponse != null ? imeResponse.getRequest() : null);
             }
 
+            if (mActivityToken != null) {
+                mService.sendActivityAssistDataToContentCapture(mActivityToken, resultData);
+            }
             mRemoteFillService.onFillRequest(request);
         }
 
@@ -615,75 +605,12 @@
     private void maybeRequestInlineSuggestionsRequestThenFillLocked(@NonNull ViewState viewState,
             int newState, int flags) {
         if (isInlineSuggestionsEnabled()) {
-            mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl();
-            mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(userId,
-                    mComponentName, mCurrentViewId, mInlineSuggestionsRequestCallback);
+            mInlineSuggestionSession.createRequest(mCurrentViewId);
         }
 
         requestNewFillResponseLocked(viewState, newState, flags);
     }
 
-    private static final class InlineSuggestionsRequestCallbackImpl
-            extends IInlineSuggestionsRequestCallback.Stub {
-        private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
-
-        private final CompletableFuture<InlineSuggestionsRequest> mRequest;
-        private final CompletableFuture<IInlineSuggestionsResponseCallback> mResponseCallback;
-
-        private InlineSuggestionsRequestCallbackImpl() {
-            mRequest = new CompletableFuture<>();
-            mResponseCallback = new CompletableFuture<>();
-        }
-
-        @Override
-        public void onInlineSuggestionsUnsupported() throws RemoteException {
-            if (sDebug) {
-                Log.d(TAG, "inline suggestions request unsupported, "
-                        + "falling back to regular autofill");
-            }
-            mRequest.cancel(true);
-            mResponseCallback.cancel(true);
-        }
-
-        @Override
-        public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
-                IInlineSuggestionsResponseCallback callback) throws RemoteException {
-            if (sDebug) {
-                Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
-            }
-            mRequest.complete(request);
-            mResponseCallback.complete(callback);
-        }
-
-        @Nullable
-        private InlineSuggestionsRequest getRequest() {
-            try {
-                return mRequest.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException e) {
-                Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
-            } catch (CancellationException e) {
-                Log.w(TAG, "Inline suggestions request cancelled");
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-            return null;
-        }
-
-        @Nullable
-        private IInlineSuggestionsResponseCallback getResponseCallback() {
-            try {
-                return mResponseCallback.get(INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException e) {
-                Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
-            } catch (CancellationException e) {
-                Log.w(TAG, "Inline suggestions callback cancelled");
-            } catch (InterruptedException | ExecutionException e) {
-                throw new RuntimeException(e);
-            }
-            return null;
-        }
-    }
-
     /**
      * Reads a new structure and then request a new fill response from the fill service.
      */
@@ -763,7 +690,6 @@
         mFlags = flags;
         this.taskId = taskId;
         this.uid = uid;
-        this.userId = userId;
         mStartTime = SystemClock.elapsedRealtime();
         mService = service;
         mLock = lock;
@@ -782,7 +708,8 @@
         mForAugmentedAutofillOnly = forAugmentedAutofillOnly;
         setClientLocked(client);
 
-        mInputMethodManagerInternal = inputMethodManagerInternal;
+        mInlineSuggestionSession = new InlineSuggestionSession(inputMethodManagerInternal, userId,
+                componentName);
 
         mMetricsLogger.write(newLogMaker(MetricsEvent.AUTOFILL_SESSION_STARTED)
                 .addTaggedData(MetricsEvent.FIELD_AUTOFILL_FLAGS, flags));
@@ -2674,11 +2601,13 @@
         }
 
         if (response.supportsInlineSuggestions()) {
-            if (requestShowInlineSuggestions(response)) {
-                //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, rather
-                // than here where framework sends back the response.
-                mService.logDatasetShown(id, mClientState);
-                return;
+            synchronized (mLock) {
+                if (requestShowInlineSuggestionsLocked(response)) {
+                    //TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
+                    // rather than here where framework sends back the response.
+                    mService.logDatasetShown(id, mClientState);
+                    return;
+                }
             }
         }
 
@@ -2716,30 +2645,32 @@
     /**
      * Returns whether we made a request to show inline suggestions.
      */
-    private boolean requestShowInlineSuggestions(FillResponse response) {
-        final IInlineSuggestionsResponseCallback inlineContentCallback =
-                mInlineSuggestionsRequestCallback != null
-                        ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
-        if (inlineContentCallback == null) {
-            Log.w(TAG, "Session input method callback is not set yet");
-            return false;
-        }
-
+    private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response) {
         final List<Dataset> datasets = response.getDatasets();
         if (datasets == null) {
             Log.w(TAG, "response returned null datasets");
             return false;
         }
 
+        final InlineSuggestionSession.ImeResponse imeResponse =
+                mInlineSuggestionSession.waitAndGetImeResponse();
+        if (imeResponse == null) {
+            Log.w(TAG, "Session input method callback is not set yet");
+            return false;
+        }
+
+        final InlineSuggestionsRequest request = imeResponse.getRequest();
         InlineSuggestionsResponse inlineSuggestionsResponse =
-                InlineSuggestionFactory.createInlineSuggestionsResponse(response.getRequestId(),
-                        datasets.toArray(new Dataset[]{}), mCurrentViewId, mContext, this, () -> {
-                    synchronized (mLock) {
-                        requestHideFillUi(mCurrentViewId);
-                    }
-                });
-        try  {
-            inlineContentCallback.onInlineSuggestionsResponse(inlineSuggestionsResponse);
+                InlineSuggestionFactory.createInlineSuggestionsResponse(request,
+                        response.getRequestId(),
+                        datasets.toArray(new Dataset[]{}), response.getInlineActions(),
+                        mCurrentViewId, mContext, this, () -> {
+                            synchronized (mLock) {
+                                requestHideFillUi(mCurrentViewId);
+                            }
+                        });
+        try {
+            imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
         } catch (RemoteException e) {
             Log.w(TAG, "onFillReady() remote error calling onInlineSuggestionsResponse()");
             return false;
@@ -3021,12 +2952,21 @@
 
         final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
 
+        // There are 3 cases when augmented autofill should ask IME for a new request:
+        // 1. standard autofill provider is None
+        // 2. standard autofill provider doesn't support inline (and returns null response)
+        // 3. standard autofill provider supports inline, but isn't called because the field
+        // doesn't want autofill
+        if (mForAugmentedAutofillOnly || !isInlineSuggestionsEnabled()) {
+            if (sDebug) Slog.d(TAG, "Create inline request for augmented autofill");
+            mInlineSuggestionSession.createRequest(mCurrentViewId);
+        }
+        InlineSuggestionSession.ImeResponse imeResponse =
+                mInlineSuggestionSession.waitAndGetImeResponse();
         final InlineSuggestionsRequest inlineSuggestionsRequest =
-                mInlineSuggestionsRequestCallback != null
-                        ? mInlineSuggestionsRequestCallback.getRequest() : null;
+                imeResponse != null ? imeResponse.getRequest() : null;
         final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
-                mInlineSuggestionsRequestCallback != null
-                        ? mInlineSuggestionsRequestCallback.getResponseCallback() : null;
+                imeResponse != null ? imeResponse.getCallback() : null;
         remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
                 currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback, () -> {
                     synchronized (mLock) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 38a5b5b..95a4a19 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -28,16 +28,20 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.autofill.AutofillId;
+import android.view.inline.InlinePresentationSpec;
 import android.view.inputmethod.InlineSuggestion;
 import android.view.inputmethod.InlineSuggestionInfo;
+import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InlineSuggestionsResponse;
+import android.widget.Toast;
 
-import com.android.internal.util.function.QuadFunction;
 import com.android.internal.view.inline.IInlineContentCallback;
 import com.android.internal.view.inline.IInlineContentProvider;
 import com.android.server.UiThread;
 
 import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiFunction;
 
 public final class InlineSuggestionFactory {
     private static final String TAG = "InlineSuggestionFactory";
@@ -57,46 +61,47 @@
      * augmented autofill service.
      */
     public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
+            @NonNull InlineSuggestionsRequest request,
             @NonNull Dataset[] datasets,
             @NonNull AutofillId autofillId,
             @NonNull Context context,
             @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
             @NonNull Runnable onErrorCallback) {
-        return createInlineSuggestionsResponseInternal(datasets, autofillId,
-                context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
-                        filedIndex) -> createAugmentedInlineSuggestion(dataset,
-                    inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback));
+        if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
+        return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
+                datasets, /* inlineActions= */ null, autofillId, context, onErrorCallback,
+                (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
     }
 
     /**
      * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
      * autofill service.
      */
-    public static InlineSuggestionsResponse createInlineSuggestionsResponse(int requestId,
+    public static InlineSuggestionsResponse createInlineSuggestionsResponse(
+            @NonNull InlineSuggestionsRequest request, int requestId,
             @NonNull Dataset[] datasets,
+            @Nullable List<InlinePresentation> inlineActions,
             @NonNull AutofillId autofillId,
             @NonNull Context context,
             @NonNull AutoFillUI.AutoFillUiCallback client,
             @NonNull Runnable onErrorCallback) {
-        return createInlineSuggestionsResponseInternal(datasets, autofillId,
-                context, onErrorCallback, (dataset, inlinePresentation, inlineSuggestionUi,
-                        filedIndex) -> createInlineSuggestion(requestId, dataset, filedIndex,
-                        inlinePresentation, inlineSuggestionUi, client));
+        if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+        return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
+                inlineActions, autofillId, context, onErrorCallback,
+                (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
     }
 
     private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
-            @NonNull Dataset[] datasets,
-            @NonNull AutofillId autofillId,
-            @NonNull Context context,
+            boolean isAugmented, @NonNull InlineSuggestionsRequest request,
+            @NonNull Dataset[] datasets, @Nullable List<InlinePresentation> inlineActions,
+            @NonNull AutofillId autofillId, @NonNull Context context,
             @NonNull Runnable onErrorCallback,
-            @NonNull QuadFunction<Dataset, InlinePresentation, InlineSuggestionUi,
-                    Integer, InlineSuggestion> suggestionFactory) {
-        if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
-
+            @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
         final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
                 onErrorCallback);
-        for (Dataset dataset : datasets) {
+        for (int i = 0; i < datasets.length; i++) {
+            final Dataset dataset = datasets[i];
             final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
             if (fieldIndex < 0) {
                 Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
@@ -108,48 +113,80 @@
                 Slog.w(TAG, "InlinePresentation not found in dataset");
                 return null;
             }
-            InlineSuggestion inlineSuggestion = suggestionFactory.apply(dataset,
-                    inlinePresentation, inlineSuggestionUi, fieldIndex);
+            InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
+                    fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
+                    inlineSuggestionUi, onClickListenerFactory);
             inlineSuggestions.add(inlineSuggestion);
         }
+        if (inlineActions != null) {
+            for (InlinePresentation inlinePresentation : inlineActions) {
+                final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
+                        mergedInlinePresentation(request, 0, inlinePresentation),
+                        inlineSuggestionUi);
+                inlineSuggestions.add(inlineAction);
+            }
+        }
         return new InlineSuggestionsResponse(inlineSuggestions);
     }
 
-    private static InlineSuggestion createAugmentedInlineSuggestion(@NonNull Dataset dataset,
+    private static InlineSuggestion createInlineAction(boolean isAugmented,
+            @NonNull Context context,
             @NonNull InlinePresentation inlinePresentation,
-            @NonNull InlineSuggestionUi inlineSuggestionUi,
-            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
+            @NonNull InlineSuggestionUi inlineSuggestionUi) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
-                InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
+                isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
+                        : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
+                InlineSuggestionInfo.TYPE_ACTION);
+        final View.OnClickListener onClickListener = v -> {
+            // TODO(b/148567875): Launch the intent provided through the slice. This
+            //  should be part of the UI renderer therefore will be moved to the support
+            //  library.
+            Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
+        };
+        return new InlineSuggestion(inlineSuggestionInfo,
+                createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
+                        onClickListener));
+    }
+
+    private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
+            @NonNull Dataset dataset,
+            int fieldIndex, @NonNull InlinePresentation inlinePresentation,
+            @NonNull InlineSuggestionUi inlineSuggestionUi,
+            @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+        // TODO(b/146453195): fill in the autofill hint properly.
+        final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
+                inlinePresentation.getInlinePresentationSpec(),
+                isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
+                        : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
                 InlineSuggestionInfo.TYPE_SUGGESTION);
-        final View.OnClickListener onClickListener = v ->
-            inlineSuggestionUiCallback.autofill(dataset);
+        final View.OnClickListener onClickListener = onClickListenerFactory.apply(dataset,
+                fieldIndex);
         final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
                         onClickListener));
         return inlineSuggestion;
     }
 
-    private static InlineSuggestion createInlineSuggestion(int requestId,
-            @NonNull Dataset dataset,
-            int fieldIndex,
-            @NonNull InlinePresentation inlinePresentation,
-            @NonNull InlineSuggestionUi inlineSuggestionUi,
-            @NonNull AutoFillUI.AutoFillUiCallback client) {
-        // TODO(b/146453195): fill in the autofill hint properly.
-        final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
-                inlinePresentation.getInlinePresentationSpec(),
-                InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
-                InlineSuggestionInfo.TYPE_SUGGESTION);
-        final View.OnClickListener onClickListener = v -> {
-            client.fill(requestId, fieldIndex, dataset);
-        };
-        final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
-                createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
-                        onClickListener));
-        return inlineSuggestion;
+    /**
+     * Returns an {@link InlinePresentation} with the style spec from the request/host, and
+     * everything else from the provided {@code inlinePresentation}.
+     */
+    private static InlinePresentation mergedInlinePresentation(
+            @NonNull InlineSuggestionsRequest request,
+            int index, @NonNull InlinePresentation inlinePresentation) {
+        final List<InlinePresentationSpec> specs = request.getPresentationSpecs();
+        if (specs.isEmpty()) {
+            return inlinePresentation;
+        }
+        InlinePresentationSpec specFromHost = specs.get(Math.min(specs.size() - 1, index));
+        InlinePresentationSpec mergedInlinePresentation = new InlinePresentationSpec.Builder(
+                inlinePresentation.getInlinePresentationSpec().getMinSize(),
+                inlinePresentation.getInlinePresentationSpec().getMaxSize()).setStyle(
+                specFromHost.getStyle()).build();
+        return new InlinePresentation(inlinePresentation.getSlice(), mergedInlinePresentation,
+                inlinePresentation.isPinned());
     }
 
     private static IInlineContentProvider.Stub createInlineContentProvider(
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
index 8d476d7..e813dae 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
@@ -48,6 +48,7 @@
         super(context);
         mOnErrorCallback = onErrorCallback;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        setFocusable(false);
     }
 
     @Override
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 583c5b5..32bca35 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -426,18 +426,26 @@
     public boolean sendActivityAssistDataLocked(@NonNull IBinder activityToken,
             @NonNull Bundle data) {
         final int id = getSessionId(activityToken);
+        final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
+        final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
+        final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
+        final SnapshotData snapshotData = new SnapshotData(assistData,
+                assistStructure, assistContent);
         if (id != NO_SESSION_ID) {
             final ContentCaptureServerSession session = mSessions.get(id);
-            final Bundle assistData = data.getBundle(ASSIST_KEY_DATA);
-            final AssistStructure assistStructure = data.getParcelable(ASSIST_KEY_STRUCTURE);
-            final AssistContent assistContent = data.getParcelable(ASSIST_KEY_CONTENT);
-            final SnapshotData snapshotData = new SnapshotData(assistData,
-                    assistStructure, assistContent);
             session.sendActivitySnapshotLocked(snapshotData);
             return true;
-        } else {
-            Slog.e(TAG, "Failed to notify activity assist data for activity: " + activityToken);
         }
+
+        // We want to send an activity snapshot regardless of whether a content capture session is
+        // present or not since a content capture session is not required for this functionality
+        if (mRemoteService != null) {
+            mRemoteService.onActivitySnapshotRequest(NO_SESSION_ID, snapshotData);
+            Slog.d(TAG, "Notified activity assist data for activity: "
+                    + activityToken + " without a session Id");
+            return true;
+        }
+
         return false;
     }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a603fa9..6fc6084 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -98,7 +98,7 @@
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.vibrator-java",
         "app-compat-annotations",
-        "framework-tethering",
+        "framework-tethering-stubs",
     ],
 
     required: [
@@ -115,8 +115,8 @@
         "android.hardware.health-V2.0-java",
         "android.hardware.light-java",
         "android.hardware.weaver-V1.0-java",
-        "android.hardware.biometrics.face-V1.0-java",
-        "android.hardware.biometrics.fingerprint-V2.1-java",
+        "android.hardware.biometrics.face-V1.1-java",
+        "android.hardware.biometrics.fingerprint-V2.2-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6726170..caacf13 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -602,7 +602,7 @@
 
     private Set<String> mWolSupportedInterfaces;
 
-    private TelephonyManager mTelephonyManager;
+    private final TelephonyManager mTelephonyManager;
     private final AppOpsManager mAppOpsManager;
 
     private final LocationPermissionChecker mLocationPermissionChecker;
@@ -961,6 +961,7 @@
         mDeps = Objects.requireNonNull(deps, "missing Dependencies");
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
+        mContext = Objects.requireNonNull(context, "missing Context");
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
@@ -989,7 +990,6 @@
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
-        mContext = Objects.requireNonNull(context, "missing Context");
         mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
         mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
         mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager");
@@ -1169,6 +1169,7 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
+        netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
         if (transportType > -1) {
             netCap.addTransportType(transportType);
         }
@@ -1699,10 +1700,12 @@
         return newLp;
     }
 
-    private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
+    private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
+            int callerUid, String callerPackageName) {
         if (!checkSettingsPermission()) {
-            nc.setSingleUid(Binder.getCallingUid());
+            nc.setSingleUid(callerUid);
         }
+        nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
         nc.setAdministratorUids(Collections.EMPTY_LIST);
 
         // Clear owner UID; this can never come from an app.
@@ -5304,7 +5307,7 @@
     // This checks that the passed capabilities either do not request a
     // specific SSID/SignalStrength, or the calling app has permission to do so.
     private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
-            int callerPid, int callerUid) {
+            int callerPid, int callerUid, String callerPackageName) {
         if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
             throw new SecurityException("Insufficient permissions to request a specific SSID");
         }
@@ -5314,6 +5317,7 @@
             throw new SecurityException(
                     "Insufficient permissions to request a specific signal strength");
         }
+        mAppOpsManager.checkPackage(callerUid, callerPackageName);
     }
 
     private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
@@ -5360,7 +5364,6 @@
             return;
         }
         MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
-        ns.assertValidFromUid(Binder.getCallingUid());
     }
 
     private void ensureValid(NetworkCapabilities nc) {
@@ -5372,7 +5375,9 @@
 
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
+            Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
+            @NonNull String callingPackageName) {
+        final int callingUid = Binder.getCallingUid();
         final NetworkRequest.Type type = (networkCapabilities == null)
                 ? NetworkRequest.Type.TRACK_DEFAULT
                 : NetworkRequest.Type.REQUEST;
@@ -5380,7 +5385,7 @@
         // the default network request. This allows callers to keep track of
         // the system default network.
         if (type == NetworkRequest.Type.TRACK_DEFAULT) {
-            networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid());
+            networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
             enforceAccessPermission();
         } else {
             networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -5392,13 +5397,14 @@
         }
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
+                Binder.getCallingPid(), callingUid, callingPackageName);
         // Set the UID range for this request to the single UID of the requester, or to an empty
         // set of UIDs if the caller has the appropriate permission and UIDs have not been set.
         // This will overwrite any allowed UIDs in the requested capabilities. Though there
         // are no visible methods to set the UIDs, an app could use reflection to try and get
         // networks for other apps so it's essential that the UIDs are overwritten.
-        restrictRequestUidsForCaller(networkCapabilities);
+        restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+                callingUid, callingPackageName);
 
         if (timeoutMs < 0) {
             throw new IllegalArgumentException("Bad timeout specified");
@@ -5473,16 +5479,18 @@
 
     @Override
     public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation) {
+            PendingIntent operation, @NonNull String callingPackageName) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
+        final int callingUid = Binder.getCallingUid();
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         enforceNetworkRequestPermissions(networkCapabilities);
         enforceMeteredApnPolicy(networkCapabilities);
         ensureRequestableCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
+                Binder.getCallingPid(), callingUid, callingPackageName);
         ensureValidNetworkSpecifier(networkCapabilities);
-        restrictRequestUidsForCaller(networkCapabilities);
+        restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+                callingUid, callingPackageName);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -5530,15 +5538,16 @@
 
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
-            Messenger messenger, IBinder binder) {
+            Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+        final int callingUid = Binder.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
 
         NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
-        restrictRequestUidsForCaller(nc);
+                Binder.getCallingPid(), callingUid, callingPackageName);
+        restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
         // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
         // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
         // onLost and onAvailable callbacks when networks move in and out of the background.
@@ -5558,17 +5567,17 @@
 
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
-            PendingIntent operation) {
+            PendingIntent operation, @NonNull String callingPackageName) {
         Objects.requireNonNull(operation, "PendingIntent cannot be null.");
+        final int callingUid = Binder.getCallingUid();
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
         ensureValid(networkCapabilities);
         ensureSufficientPermissionsForRequest(networkCapabilities,
-                Binder.getCallingPid(), Binder.getCallingUid());
-
+                Binder.getCallingPid(), callingUid, callingPackageName);
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
-        restrictRequestUidsForCaller(nc);
+        restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
@@ -7859,12 +7868,13 @@
             throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
                     + " Please use NetworkCapabilities instead.");
         }
-        mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
+        final int callingUid = Binder.getCallingUid();
+        mAppOpsManager.checkPackage(callingUid, callingPackageName);
 
         // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
         // and administrator uids to be safe.
         final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
-        restrictRequestUidsForCaller(nc);
+        restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
 
         final NetworkRequest requestWithId =
                 new NetworkRequest(
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0ab8af6a..63cddac 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -30,6 +30,7 @@
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -2170,10 +2171,10 @@
 
     @Override
     public boolean injectLocation(Location location) {
-        mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
-                "Location Hardware permission not granted to inject location");
-        mContext.enforceCallingPermission(ACCESS_FINE_LOCATION,
-                "Access Fine Location permission not granted to inject Location");
+        mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, null);
+
+        Preconditions.checkArgument(location.isComplete());
 
         synchronized (mLock) {
             LocationProviderManager manager = getLocationProviderManager(location.getProvider());
@@ -2419,20 +2420,15 @@
 
     @Override
     public boolean isLocationEnabledForUser(int userId) {
-        if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
-                    null);
-        }
-
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, false, "isLocationEnabledForUser", null);
         return mSettingsHelper.isLocationEnabled(userId);
     }
 
     @Override
     public boolean isProviderEnabledForUser(String providerName, int userId) {
-        if (UserHandle.getCallingUserId() != userId) {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
-                    null);
-        }
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, false, "isProviderEnabledForUser", null);
 
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
@@ -2741,6 +2737,9 @@
 
     @Override
     public void setTestProviderLocation(String provider, Location location, String packageName) {
+        Preconditions.checkArgument(location.isComplete(),
+                "incomplete location object, missing timestamp or accuracy?");
+
         if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName)
                 != AppOpsManager.MODE_ALLOWED) {
             return;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index cbf6c27..0e5a6bb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -284,17 +284,17 @@
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
                         | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
-                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST
+                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
+
+    static final int ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK =
+                PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+                        | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
+                        | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
+                        | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
+                        | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES
                         | PhoneStateListener.LISTEN_REGISTRATION_FAILURE
                         | PhoneStateListener.LISTEN_BARRING_INFO;
 
-    static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_PRECISE_CALL_STATE
-                | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE
-                | PhoneStateListener.LISTEN_CALL_DISCONNECT_CAUSES
-                | PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED
-                | PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES;
-
     static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
                     | PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_SMS;
@@ -2535,7 +2535,7 @@
             }
         }
 
-        if ((events & PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
+        if ((events & ENFORCE_PRECISE_PHONE_STATE_PERMISSION_MASK) != 0) {
             // check if calling app has either permission READ_PRECISE_PHONE_STATE
             // or with carrier privileges
             try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a6a569c..d5a7253 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -466,18 +466,9 @@
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
-    // How long we wait for an attached process to publish its content providers
-    // before we decide it must be hung.
-    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
-
     // How long we wait to kill an application zygote, after the last process using
     // it has gone away.
     static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
-    /**
-     * How long we wait for an provider to be published. Should be longer than
-     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
-     */
-    static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
@@ -4968,7 +4959,8 @@
         if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
             Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
             msg.obj = app;
-            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
+            mHandler.sendMessageDelayed(msg,
+                    ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
         }
 
         checkTime(startTime, "attachApplicationLocked: before bindApplication");
@@ -7247,7 +7239,8 @@
         }
 
         // Wait for the provider to be published...
-        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
+        final long timeout =
+                SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
         boolean timedOut = false;
         synchronized (cpr) {
             while (cpr.provider == null) {
@@ -7284,12 +7277,14 @@
             }
         }
         if (timedOut) {
-            // Note we do it afer releasing the lock.
+            // Note we do it after releasing the lock.
             String callerName = "unknown";
-            synchronized (this) {
-                final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
-                if (record != null) {
-                    callerName = record.processName;
+            if (caller != null) {
+                synchronized (this) {
+                    final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
+                    if (record != null) {
+                        callerName = record.processName;
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 60f420e..e17c1f8 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -24,7 +24,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
 import android.media.AudioManager;
 import android.media.AudioRoutesInfo;
 import android.media.AudioSystem;
@@ -402,7 +402,7 @@
     }
 
     /*package*/ int setPreferredDeviceForStrategySync(int strategy,
-                                                      @NonNull AudioDeviceAddress device) {
+                                                      @NonNull AudioDevice device) {
         return mDeviceInventory.setPreferredDeviceForStrategySync(strategy, device);
     }
 
@@ -543,7 +543,7 @@
         sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
     }
 
-    /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDeviceAddress device)
+    /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDevice device)
     {
         sendILMsgNoDelay(MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY, SENDMSG_QUEUE, strategy, device);
     }
@@ -904,7 +904,7 @@
                 } break;
                 case MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY: {
                     final int strategy = msg.arg1;
-                    final AudioDeviceAddress device = (AudioDeviceAddress) msg.obj;
+                    final AudioDevice device = (AudioDevice) msg.obj;
                     mDeviceInventory.onSaveSetPreferredDevice(strategy, device);
                 } break;
                 case MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY: {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 75d9dd8..1f998c3 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -23,7 +23,7 @@
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.content.Intent;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
 import android.media.AudioDevicePort;
 import android.media.AudioFormat;
 import android.media.AudioManager;
@@ -75,7 +75,7 @@
     private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>();
 
     // List of preferred devices for strategies
-    private final ArrayMap<Integer, AudioDeviceAddress> mPreferredDevices = new ArrayMap<>();
+    private final ArrayMap<Integer, AudioDevice> mPreferredDevices = new ArrayMap<>();
 
     // the wrapper for AudioSystem static methods, allows us to spy AudioSystem
     private final @NonNull AudioSystemAdapter mAudioSystem;
@@ -468,7 +468,7 @@
         }
     }
 
-    /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDeviceAddress device) {
+    /*package*/ void onSaveSetPreferredDevice(int strategy, @NonNull AudioDevice device) {
         mPreferredDevices.put(strategy, device);
     }
 
@@ -480,7 +480,7 @@
     //
 
     /*package*/ int setPreferredDeviceForStrategySync(int strategy,
-                                                      @NonNull AudioDeviceAddress device) {
+                                                      @NonNull AudioDevice device) {
         final long identity = Binder.clearCallingIdentity();
         final int status = mAudioSystem.setPreferredDeviceForStrategy(strategy, device);
         Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 82a2f01..342ce22 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -65,7 +65,7 @@
 import android.hidl.manager.V1_0.IServiceManager;
 import android.media.AudioAttributes;
 import android.media.AudioAttributes.AttributeSystemUsage;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
 import android.media.AudioDeviceInfo;
 import android.media.AudioFocusInfo;
 import android.media.AudioFocusRequest;
@@ -1712,7 +1712,7 @@
     // IPC methods
     ///////////////////////////////////////////////////////////////////////////
     /** @see AudioManager#setPreferredDeviceForStrategy(AudioProductStrategy, AudioDeviceInfo) */
-    public int setPreferredDeviceForStrategy(int strategy, AudioDeviceAddress device) {
+    public int setPreferredDeviceForStrategy(int strategy, AudioDevice device) {
         if (device == null) {
             return AudioSystem.ERROR;
         }
@@ -1721,7 +1721,7 @@
                 "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
                 Binder.getCallingUid(), Binder.getCallingPid(), strategy, device.toString());
         sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
-        if (device.getRole() == AudioDeviceAddress.ROLE_INPUT) {
+        if (device.getRole() == AudioDevice.ROLE_INPUT) {
             Log.e(TAG, "Unsupported input routing in " + logString);
             return AudioSystem.ERROR;
         }
@@ -1749,9 +1749,9 @@
     }
 
     /** @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy) */
-    public AudioDeviceAddress getPreferredDeviceForStrategy(int strategy) {
+    public AudioDevice getPreferredDeviceForStrategy(int strategy) {
         enforceModifyAudioRoutingPermission();
-        AudioDeviceAddress[] devices = new AudioDeviceAddress[1];
+        AudioDevice[] devices = new AudioDevice[1];
         final long identity = Binder.clearCallingIdentity();
         final int status = AudioSystem.getPreferredDeviceForStrategy(strategy, devices);
         Binder.restoreCallingIdentity(identity);
@@ -1765,7 +1765,7 @@
     }
 
     /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
-    public @NonNull ArrayList<AudioDeviceAddress> getDevicesForAttributes(
+    public @NonNull ArrayList<AudioDevice> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
         Objects.requireNonNull(attributes);
         enforceModifyAudioRoutingPermission();
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 9d06b42..a3086c0 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,7 +17,7 @@
 package com.android.server.audio;
 
 import android.annotation.NonNull;
-import android.media.AudioDeviceAddress;
+import android.media.AudioDevice;
 import android.media.AudioSystem;
 import android.util.Log;
 
@@ -86,12 +86,12 @@
     }
 
     /**
-     * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDeviceAddress)}
+     * Same as {@link AudioSystem#setPreferredDeviceForStrategy(int, AudioDevice)}
      * @param strategy
      * @param device
      * @return
      */
-    public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) {
+    public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDevice device) {
         return AudioSystem.setPreferredDeviceForStrategy(strategy, device);
     }
 
@@ -138,7 +138,7 @@
         }
 
         @Override
-        public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDeviceAddress device) {
+        public int setPreferredDeviceForStrategy(int strategy, @NonNull AudioDevice device) {
             return AudioSystem.AUDIO_STATUS_OK;
         }
 
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 0d88388..0f54984 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -99,6 +99,33 @@
         public String[] getConfiguration(Context context) {
             return context.getResources().getStringArray(R.array.config_biometric_sensors);
         }
+
+        /**
+         * Allows us to mock FingerprintService for testing
+         */
+        @VisibleForTesting
+        public IFingerprintService getFingerprintService() {
+            return IFingerprintService.Stub.asInterface(
+                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+        }
+
+        /**
+         * Allows us to mock FaceService for testing
+         */
+        @VisibleForTesting
+        public IFaceService getFaceService() {
+            return IFaceService.Stub.asInterface(
+                    ServiceManager.getService(Context.FACE_SERVICE));
+        }
+
+        /**
+         * Allows us to mock IrisService for testing
+         */
+        @VisibleForTesting
+        public IIrisService getIrisService() {
+            return IIrisService.Stub.asInterface(
+                    ServiceManager.getService(Context.IRIS_SERVICE));
+        }
     }
 
     private final class AuthServiceImpl extends IAuthService.Stub {
@@ -178,7 +205,6 @@
 
         mInjector = injector;
         mImpl = new AuthServiceImpl();
-        final PackageManager pm = context.getPackageManager();
     }
 
     private void registerAuthenticator(SensorConfig config) throws RemoteException {
@@ -191,18 +217,36 @@
 
         switch (config.mModality) {
             case TYPE_FINGERPRINT:
-                authenticator = new FingerprintAuthenticator(IFingerprintService.Stub.asInterface(
-                        ServiceManager.getService(Context.FINGERPRINT_SERVICE)));
+                final IFingerprintService fingerprintService = mInjector.getFingerprintService();
+                if (fingerprintService == null) {
+                    Slog.e(TAG, "Attempting to register with null FingerprintService. Please check"
+                            + " your device configuration.");
+                    return;
+                }
+
+                authenticator = new FingerprintAuthenticator(fingerprintService);
                 break;
 
             case TYPE_FACE:
-                authenticator = new FaceAuthenticator(IFaceService.Stub.asInterface(
-                        ServiceManager.getService(Context.FACE_SERVICE)));
+                final IFaceService faceService = mInjector.getFaceService();
+                if (faceService == null) {
+                    Slog.e(TAG, "Attempting to register with null FaceService. Please check your"
+                            + " device configuration.");
+                    return;
+                }
+
+                authenticator = new FaceAuthenticator(faceService);
                 break;
 
             case TYPE_IRIS:
-                authenticator = new IrisAuthenticator(IIrisService.Stub.asInterface(
-                        ServiceManager.getService(Context.IRIS_SERVICE)));
+                final IIrisService irisService = mInjector.getIrisService();
+                if (irisService == null) {
+                    Slog.e(TAG, "Attempting to register with null IrisService. Please check your"
+                            + " device configuration.");
+                    return;
+                }
+
+                authenticator = new IrisAuthenticator(irisService);
                 break;
 
             default:
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 766e5c4..7bbda9f 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -20,11 +20,14 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.os.IBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.security.KeyStore;
 import android.util.Slog;
 
+import java.io.IOException;
 import java.util.ArrayList;
 
 /**
@@ -41,6 +44,7 @@
     public static final int LOCKOUT_PERMANENT = 2;
 
     private final boolean mRequireConfirmation;
+    private final NativeHandle mWindowId;
 
     // We need to track this state since it's possible for applications to request for
     // authentication while the device is already locked out. In that case, the client is created
@@ -69,11 +73,25 @@
     public AuthenticationClient(Context context, Constants constants,
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId,
-            boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+            boolean restricted, String owner, int cookie, boolean requireConfirmation,
+            IBiometricNativeHandle windowId) {
         super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId,
                 restricted, owner, cookie);
         mOpId = opId;
         mRequireConfirmation = requireConfirmation;
+        mWindowId = Utils.dupNativeHandle(windowId);
+    }
+
+    @Override
+    public void destroy() {
+        if (mWindowId != null && mWindowId.getFileDescriptors() != null) {
+            try {
+                mWindowId.close();
+            } catch (IOException e) {
+                Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e);
+            }
+        }
+        super.destroy();
     }
 
     protected long getStartTimeMs() {
@@ -233,7 +251,7 @@
         onStart();
         try {
             mStartTimeMs = System.currentTimeMillis();
-            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId());
+            final int result = getDaemonWrapper().authenticate(mOpId, getGroupId(), mWindowId);
             if (result != 0) {
                 Slog.w(getLogTag(), "startAuthentication failed, result=" + result);
                 mMetricsLogger.histogram(mConstants.tagAuthStartError(), result);
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 687d935..0e70994 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -32,6 +32,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
@@ -43,6 +44,7 @@
 import android.os.IBinder;
 import android.os.IHwBinder;
 import android.os.IRemoteCallback;
+import android.os.NativeHandle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
@@ -220,9 +222,10 @@
 
         public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+                boolean restricted, String owner, int cookie, boolean requireConfirmation,
+                IBiometricNativeHandle windowId) {
             super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId,
-                    groupId, opId, restricted, owner, cookie, requireConfirmation);
+                    groupId, opId, restricted, owner, cookie, requireConfirmation, windowId);
         }
 
         @Override
@@ -283,10 +286,10 @@
         public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId,
                 IBinder token, ServiceListener listener, int userId, int groupId,
                 byte[] cryptoToken, boolean restricted, String owner,
-                final int[] disabledFeatures, int timeoutSec) {
+                final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) {
             super(context, getConstants(), daemon, halDeviceId, token, listener,
                     userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(),
-                    disabledFeatures, timeoutSec);
+                    disabledFeatures, timeoutSec, windowId);
         }
 
         @Override
@@ -472,12 +475,13 @@
      */
     protected interface DaemonWrapper {
         int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h.
-        int authenticate(long operationId, int groupId) throws RemoteException;
+        int authenticate(long operationId, int groupId, NativeHandle windowId)
+                throws RemoteException;
         int cancel() throws RemoteException;
         int remove(int groupId, int biometricId) throws RemoteException;
         int enumerate() throws RemoteException;
         int enroll(byte[] token, int groupId, int timeout,
-                ArrayList<Integer> disabledFeatures) throws RemoteException;
+                ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException;
         void resetLockout(byte[] token) throws RemoteException;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java
index 7ebb7c0..684795e 100644
--- a/services/core/java/com/android/server/biometrics/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/EnrollClient.java
@@ -20,10 +20,13 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.os.IBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.util.Slog;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -35,6 +38,7 @@
     private final BiometricUtils mBiometricUtils;
     private final int[] mDisabledFeatures;
     private final int mTimeoutSec;
+    private final NativeHandle mWindowId;
 
     private long mEnrollmentStartTimeMs;
 
@@ -44,13 +48,26 @@
             BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token,
             BiometricServiceBase.ServiceListener listener, int userId, int groupId,
             byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils,
-            final int[] disabledFeatures, int timeoutSec) {
+            final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) {
         super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted,
                 owner, 0 /* cookie */);
         mBiometricUtils = utils;
         mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
         mTimeoutSec = timeoutSec;
+        mWindowId = Utils.dupNativeHandle(windowId);
+    }
+
+    @Override
+    public void destroy() {
+        if (mWindowId != null && mWindowId.getFileDescriptors() != null) {
+            try {
+                mWindowId.close();
+            } catch (IOException e) {
+                Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e);
+            }
+        }
+        super.destroy();
     }
 
     @Override
@@ -102,7 +119,7 @@
             }
 
             final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec,
-                    disabledFeatures);
+                    disabledFeatures, mWindowId);
             if (result != 0) {
                 Slog.w(getLogTag(), "startEnroll failed, result=" + result);
                 mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result);
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 389763b..2d4ab63 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -23,12 +23,17 @@
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.NativeHandle;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
+
 public class Utils {
     public static boolean isDebugEnabled(Context context, int targetUserId) {
         if (targetUserId == UserHandle.USER_NULL) {
@@ -237,4 +242,31 @@
                 throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
         }
     }
+
+    /**
+     * Converts an {@link IBiometricNativeHandle} to a {@link NativeHandle} by duplicating the
+     * the underlying file descriptors.
+     *
+     * Both the original and new handle must be closed after use.
+     *
+     * @param h {@link IBiometricNativeHandle} received as a binder call argument. Usually used to
+     *          identify a WindowManager window. Can be null.
+     * @return A {@link NativeHandle} representation of {@code h}. Will be null if either {@code h}
+     *          or its contents are null.
+     */
+    public static NativeHandle dupNativeHandle(IBiometricNativeHandle h) {
+        NativeHandle handle = null;
+        if (h != null && h.fds != null && h.ints != null) {
+            FileDescriptor[] fds = new FileDescriptor[h.fds.length];
+            for (int i = 0; i < h.fds.length; ++i) {
+                try {
+                    fds[i] = h.fds[i].dup().getFileDescriptor();
+                } catch (IOException e) {
+                    return null;
+                }
+            }
+            handle = new NativeHandle(fds, h.ints, true /* own */);
+        }
+        return handle;
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index b512475..31c3d4d 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -34,6 +34,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
@@ -214,9 +215,10 @@
         public FaceAuthClient(Context context,
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
-                boolean restricted, String owner, int cookie, boolean requireConfirmation) {
+                boolean restricted, String owner, int cookie, boolean requireConfirmation,
+                IBiometricNativeHandle windowId) {
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
-                    restricted, owner, cookie, requireConfirmation);
+                    restricted, owner, cookie, requireConfirmation, windowId);
         }
 
         @Override
@@ -373,7 +375,7 @@
         @Override // Binder call
         public void enroll(int userId, final IBinder token, final byte[] cryptoToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
-                final int[] disabledFeatures) {
+                final int[] disabledFeatures, IBiometricNativeHandle windowId) {
             checkPermission(MANAGE_BIOMETRIC);
             updateActiveGroup(userId, opPackageName);
 
@@ -384,7 +386,7 @@
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId,
                     0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures,
-                    ENROLL_TIMEOUT_SEC) {
+                    ENROLL_TIMEOUT_SEC, windowId) {
 
                 @Override
                 public int[] getAcquireIgnorelist() {
@@ -411,6 +413,14 @@
         }
 
         @Override // Binder call
+        public void enrollRemotely(int userId, final IBinder token, final byte[] cryptoToken,
+                final IFaceServiceReceiver receiver, final String opPackageName,
+                final int[] disabledFeatures) {
+            checkPermission(MANAGE_BIOMETRIC);
+            // TODO(b/145027036): Implement this.
+        }
+
+        @Override // Binder call
         public void cancelEnrollment(final IBinder token) {
             checkPermission(MANAGE_BIOMETRIC);
             cancelEnrollmentInternal(token);
@@ -426,7 +436,7 @@
             final AuthenticationClientImpl client = new FaceAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
-                    0 /* cookie */, false /* requireConfirmation */);
+                    0 /* cookie */, false /* requireConfirmation */, null /* windowId */);
             authenticateInternal(client, opId, opPackageName);
         }
 
@@ -442,7 +452,7 @@
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(wrapperReceiver),
                     mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie,
-                    requireConfirmation);
+                    requireConfirmation, null /* windowId */);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -985,7 +995,8 @@
      */
     private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
         @Override
-        public int authenticate(long operationId, int groupId) throws RemoteException {
+        public int authenticate(long operationId, int groupId, NativeHandle windowId)
+                throws RemoteException {
             IBiometricsFace daemon = getFaceDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "authenticate(): no face HAL!");
@@ -1026,7 +1037,7 @@
 
         @Override
         public int enroll(byte[] cryptoToken, int groupId, int timeout,
-                ArrayList<Integer> disabledFeatures) throws RemoteException {
+                ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException {
             IBiometricsFace daemon = getFaceDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "enroll(): no face HAL!");
@@ -1036,7 +1047,17 @@
             for (int i = 0; i < cryptoToken.length; i++) {
                 token.add(cryptoToken[i]);
             }
-            return daemon.enroll(token, timeout, disabledFeatures);
+            android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 =
+                    android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom(
+                            daemon);
+            if (daemon11 != null) {
+                return daemon11.enroll_1_1(token, timeout, disabledFeatures, windowId);
+            } else if (windowId == null) {
+                return daemon.enroll(token, timeout, disabledFeatures);
+            } else {
+                Slog.e(TAG, "enroll(): windowId is only supported in @1.1 HAL");
+                return ERROR_ESRCH;
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
index 6150de1..7a4e62e 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java
@@ -38,7 +38,7 @@
             String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId)
             throws RemoteException {
         mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver,
-                opPackageName, cookie, callingUid, callingPid, callingUserId);
+                opPackageName, cookie, callingUid, callingPid, callingUserId, null /* windowId */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 44797ad..57d1867 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -37,6 +37,7 @@
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.IBiometricNativeHandle;
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
@@ -50,6 +51,7 @@
 import android.os.Build;
 import android.os.Environment;
 import android.os.IBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.os.SELinux;
 import android.os.SystemClock;
@@ -132,9 +134,9 @@
                 DaemonWrapper daemon, long halDeviceId, IBinder token,
                 ServiceListener listener, int targetUserId, int groupId, long opId,
                 boolean restricted, String owner, int cookie,
-                boolean requireConfirmation) {
+                boolean requireConfirmation, IBiometricNativeHandle windowId) {
             super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId,
-                    restricted, owner, cookie, requireConfirmation);
+                    restricted, owner, cookie, requireConfirmation, windowId);
         }
 
         @Override
@@ -198,7 +200,7 @@
         @Override // Binder call
         public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
                 final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName) {
+                final String opPackageName, IBiometricNativeHandle windowId) {
             checkPermission(MANAGE_FINGERPRINT);
 
             final boolean restricted = isRestricted();
@@ -206,7 +208,7 @@
             final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
                     mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId,
                     cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */,
-                    ENROLL_TIMEOUT_SEC) {
+                    ENROLL_TIMEOUT_SEC, windowId) {
                 @Override
                 public boolean shouldVibrate() {
                     return true;
@@ -230,20 +232,22 @@
         @Override // Binder call
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName) {
+                final String opPackageName, IBiometricNativeHandle windowId) {
             updateActiveGroup(groupId, opPackageName);
             final boolean restricted = isRestricted();
             final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(),
                     mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
                     mCurrentUserId, groupId, opId, restricted, opPackageName,
-                    0 /* cookie */, false /* requireConfirmation */);
+                    0 /* cookie */, false /* requireConfirmation */,
+                    windowId);
             authenticateInternal(client, opId, opPackageName);
         }
 
         @Override // Binder call
         public void prepareForAuthentication(IBinder token, long opId, int groupId,
                 IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName,
-                int cookie, int callingUid, int callingPid, int callingUserId) {
+                int cookie, int callingUid, int callingPid, int callingUserId,
+                IBiometricNativeHandle windowId) {
             checkPermission(MANAGE_BIOMETRIC);
             updateActiveGroup(groupId, opPackageName);
             final boolean restricted = true; // BiometricPrompt is always restricted
@@ -251,7 +255,8 @@
                     mDaemonWrapper, mHalDeviceId, token,
                     new BiometricPromptServiceListenerImpl(wrapperReceiver),
                     mCurrentUserId, groupId, opId, restricted, opPackageName, cookie,
-                    false /* requireConfirmation */);
+                    false /* requireConfirmation */,
+                    windowId);
             authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
                     callingUserId);
         }
@@ -654,13 +659,24 @@
      */
     private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() {
         @Override
-        public int authenticate(long operationId, int groupId) throws RemoteException {
+        public int authenticate(long operationId, int groupId, NativeHandle windowId)
+                throws RemoteException {
             IBiometricsFingerprint daemon = getFingerprintDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "authenticate(): no fingerprint HAL!");
                 return ERROR_ESRCH;
             }
-            return daemon.authenticate(operationId, groupId);
+            android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 =
+                    android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom(
+                            daemon);
+            if (daemon22 != null) {
+                return daemon22.authenticate_2_2(operationId, groupId, windowId);
+            } else if (windowId == null) {
+                return daemon.authenticate(operationId, groupId);
+            } else {
+                Slog.e(TAG, "authenticate(): windowId is only supported in @2.2 HAL");
+                return ERROR_ESRCH;
+            }
         }
 
         @Override
@@ -695,13 +711,27 @@
 
         @Override
         public int enroll(byte[] cryptoToken, int groupId, int timeout,
-                ArrayList<Integer> disabledFeatures) throws RemoteException {
+                ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException {
             IBiometricsFingerprint daemon = getFingerprintDaemon();
             if (daemon == null) {
                 Slog.w(TAG, "enroll(): no fingerprint HAL!");
                 return ERROR_ESRCH;
             }
-            return daemon.enroll(cryptoToken, groupId, timeout);
+            android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 =
+                    android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom(
+                            daemon);
+            if (daemon22 != null) {
+                ArrayList<Byte> cryptoTokenAsList = new ArrayList<>(cryptoToken.length);
+                for (byte b : cryptoToken) {
+                    cryptoTokenAsList.add(b);
+                }
+                return daemon22.enroll_2_2(cryptoTokenAsList, groupId, timeout, windowId);
+            } else if (windowId == null) {
+                return daemon.enroll(cryptoToken, groupId, timeout);
+            } else {
+                Slog.e(TAG, "enroll(): windowId is only supported in @2.2 HAL");
+                return ERROR_ESRCH;
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 2992f1e..a7e1a28 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -291,7 +291,7 @@
                 mWfdEnabling = true;
 
                 WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
-                wfdInfo.setWfdEnabled(true);
+                wfdInfo.setEnabled(true);
                 wfdInfo.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE);
                 wfdInfo.setSessionAvailable(true);
                 wfdInfo.setControlPort(DEFAULT_CONTROL_PORT);
@@ -323,7 +323,7 @@
             // WFD should be disabled.
             if (mWfdEnabled || mWfdEnabling) {
                 WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo();
-                wfdInfo.setWfdEnabled(false);
+                wfdInfo.setEnabled(false);
                 mWifiP2pManager.setWfdInfo(mWifiP2pChannel, wfdInfo, new ActionListener() {
                     @Override
                     public void onSuccess() {
@@ -1044,7 +1044,7 @@
     private static boolean isWifiDisplay(WifiP2pDevice device) {
         WifiP2pWfdInfo wfdInfo = device.getWfdInfo();
         return wfdInfo != null
-                && wfdInfo.isWfdEnabled()
+                && wfdInfo.isEnabled()
                 && isPrimarySinkDeviceType(wfdInfo.getDeviceType());
     }
 
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 9754b6d..0450647 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -39,13 +39,20 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.content.pm.parsing.ApkParseUtils;
+import android.content.pm.parsing.PackageInfoUtils;
+import android.content.pm.parsing.ParsedPackage;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Slog;
 
@@ -67,6 +74,7 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -121,11 +129,11 @@
                 IntegrityFileManager.getInstance(),
                 handlerThread.getThreadHandler(),
                 Settings.Global.getInt(
-                                    context.getContentResolver(),
-                                    Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
-                                    0)
-                            == 1
-                );
+                        context.getContentResolver(),
+                        Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER,
+                        0)
+                        == 1
+        );
     }
 
     @VisibleForTesting
@@ -260,16 +268,25 @@
                 return;
             }
 
-            String appCert = getCertificateFingerprint(packageInfo);
+            List<String> appCertificates = getCertificateFingerprint(packageInfo);
+            List<String> installerCertificates =
+                    getInstallerCertificateFingerprint(installerPackageName);
+
+            // TODO (b/148373316): Figure out what field contains which fields are populated for
+            // rotated and the multiple signers. Until then, return the first certificate.
+            String appCert = appCertificates.isEmpty() ? "" : appCertificates.get(0);
+            String installerCert =
+                    installerCertificates.isEmpty() ? "" : installerCertificates.get(0);
+
+            Slog.w(TAG, appCertificates.toString());
 
             AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
 
             builder.setPackageName(getPackageNameNormalized(packageName));
-            builder.setAppCertificate(appCert == null ? "" : appCert);
+            builder.setAppCertificate(appCert);
             builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1));
             builder.setInstallerName(getPackageNameNormalized(installerPackageName));
-            builder.setInstallerCertificate(
-                    getInstallerCertificateFingerprint(installerPackageName));
+            builder.setInstallerCertificate(installerCert);
             builder.setIsPreInstalled(isSystemApp(packageName));
 
             AppInstallMetadata appInstallMetadata = builder.build();
@@ -320,7 +337,7 @@
      * Verify the UID and return the installer package name.
      *
      * @return the package name of the installer, or null if it cannot be determined or it is
-     *     installed via adb.
+     * installed via adb.
      */
     @Nullable
     private String getInstallerPackageName(Intent intent) {
@@ -399,25 +416,29 @@
         }
     }
 
-    private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
-        return getFingerprint(getSignature(packageInfo));
-    }
-
-    private String getInstallerCertificateFingerprint(String installer) {
+    private List<String> getInstallerCertificateFingerprint(String installer) {
         if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) {
-            return INSTALLER_CERT_NOT_APPLICABLE;
+            return Collections.emptyList();
         }
         try {
             PackageInfo installerInfo =
                     mContext.getPackageManager()
-                            .getPackageInfo(installer, PackageManager.GET_SIGNATURES);
+                            .getPackageInfo(installer, PackageManager.GET_SIGNING_CERTIFICATES);
             return getCertificateFingerprint(installerInfo);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.i(TAG, "Installer package " + installer + " not found.");
-            return "";
+            return Collections.emptyList();
         }
     }
 
+    private List<String> getCertificateFingerprint(@NonNull PackageInfo packageInfo) {
+        ArrayList<String> certificateFingerprints = new ArrayList();
+        for (Signature signature : getSignatures(packageInfo)) {
+            certificateFingerprints.add(getFingerprint(signature));
+        }
+        return certificateFingerprints;
+    }
+
     /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */
     private Map<String, String> getAllowedInstallers(@NonNull PackageInfo packageInfo) {
         Map<String, String> packageCertMap = new HashMap<>();
@@ -445,12 +466,15 @@
         return packageCertMap;
     }
 
-    private static Signature getSignature(@NonNull PackageInfo packageInfo) {
-        if (packageInfo.signatures == null || packageInfo.signatures.length < 1) {
+    private static Signature[] getSignatures(@NonNull PackageInfo packageInfo) {
+        SigningInfo signingInfo = packageInfo.signingInfo;
+
+        if (signingInfo == null || signingInfo.getApkContentsSigners().length < 1) {
             throw new IllegalArgumentException("Package signature not found in " + packageInfo);
         }
-        // Only the first element is guaranteed to be present.
-        return packageInfo.signatures[0];
+
+        // We are only interested in evaluating the active signatures.
+        return signingInfo.getApkContentsSigners();
     }
 
     private static String getFingerprint(Signature cert) {
@@ -489,20 +513,14 @@
         if (installationPath == null) {
             throw new IllegalArgumentException("Installation path is null, package not found");
         }
-        PackageInfo packageInfo;
+
+        PackageParser parser = new PackageParser();
         try {
-            // The installation path will be a directory for a multi-apk install on L+
-            if (installationPath.isDirectory()) {
-                packageInfo = getMultiApkInfo(installationPath);
-            } else {
-                packageInfo =
-                        mContext.getPackageManager()
-                                .getPackageArchiveInfo(
-                                        installationPath.getPath(),
-                                        PackageManager.GET_SIGNATURES
-                                                | PackageManager.GET_META_DATA);
-            }
-            return packageInfo;
+            ParsedPackage pkg = parser.parseParsedPackage(installationPath, 0, false);
+            int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA;
+            ApkParseUtils.collectCertificates(pkg, false);
+            return PackageInfoUtils.generate(pkg, null, flags, 0, 0, null, new PackageUserState(),
+                    UserHandle.getCallingUserId());
         } catch (Exception e) {
             throw new IllegalArgumentException("Exception reading " + dataUri, e);
         }
@@ -515,7 +533,8 @@
                 mContext.getPackageManager()
                         .getPackageArchiveInfo(
                                 baseFile.getAbsolutePath(),
-                                PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA);
+                                PackageManager.GET_SIGNING_CERTIFICATES
+                                        | PackageManager.GET_META_DATA);
 
         if (basePackageInfo == null) {
             for (File apkFile : multiApkDirectory.listFiles()) {
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 669f1ac..6474303 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.media.AudioManager;
 import android.media.MediaRoute2Info;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -55,6 +56,7 @@
     private final Context mContext;
     private final BluetoothAdapter mBluetoothAdapter;
     private final BluetoothRoutesUpdatedListener mListener;
+    private final AudioManager mAudioManager;
     private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>();
     private final IntentFilter mIntentFilter = new IntentFilter();
     private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
@@ -82,6 +84,7 @@
         mContext = context;
         mBluetoothAdapter = btAdapter;
         mListener = listener;
+        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         buildBluetoothRoutes();
 
         mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
@@ -181,12 +184,15 @@
     private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
         BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
         newBtRoute.btDevice = device;
+        // Current / Max volume will be set when connected.
+        // TODO: Is there any BT device which has fixed volume?
         newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName())
                 .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
                 .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
                 .setDescription(mContext.getResources().getText(
                         R.string.bluetooth_a2dp_audio_route_name).toString())
                 .setDeviceType(MediaRoute2Info.DEVICE_TYPE_BLUETOOTH)
+                .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                 .build();
         newBtRoute.connectedProfiles = new SparseBooleanArray();
         return newBtRoute;
@@ -203,10 +209,20 @@
             Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
             return;
         }
-        if (btRoute.route.getConnectionState() != state) {
-            btRoute.route = new MediaRoute2Info.Builder(btRoute.route)
-                    .setConnectionState(state).build();
+        if (btRoute.route.getConnectionState() == state) {
+            return;
         }
+
+        // Update volume when the connection state is changed.
+        MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.route)
+                .setConnectionState(state);
+
+        if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
+            int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
+            int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
+            builder.setVolumeMax(maxVolume).setVolume(currentVolume);
+        }
+        btRoute.route = builder.build();
     }
 
     interface BluetoothRoutesUpdatedListener {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 888f7ce..b5dcea8 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -20,9 +20,11 @@
 import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
 
 import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioRoutesObserver;
@@ -107,6 +109,9 @@
             }
         });
         initializeSessionInfo();
+
+        mContext.registerReceiver(new VolumeChangeReceiver(),
+                new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
     }
 
     @Override
@@ -278,4 +283,44 @@
         }
         mCallback.onSessionUpdated(this, sessionInfo);
     }
+
+    private class VolumeChangeReceiver extends BroadcastReceiver {
+        // This will be called in the main thread.
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+                return;
+            }
+
+            final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+            if (streamType != AudioManager.STREAM_MUSIC) {
+                return;
+            }
+
+            final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+            final int oldVolume = intent.getIntExtra(
+                    AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
+
+            if (newVolume != oldVolume) {
+                String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+                if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
+                    for (int i = mBluetoothRoutes.size() - 1; i >= 0; i--) {
+                        MediaRoute2Info route = mBluetoothRoutes.get(i);
+                        if (TextUtils.equals(activeBtDeviceAddress, route.getId())) {
+                            mBluetoothRoutes.set(i,
+                                    new MediaRoute2Info.Builder(route)
+                                            .setVolume(newVolume)
+                                            .build());
+                            break;
+                        }
+                    }
+                } else {
+                    mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
+                            .setVolume(newVolume)
+                            .build();
+                }
+                publishRoutes();
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index e8cb163..061cbd8 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -170,6 +170,11 @@
         mFileWriteHandler.post(rpr);
     }
 
+    public void deleteNotificationHistoryItem(String pkg, long postedTime) {
+        RemoveNotificationRunnable rnr = new RemoveNotificationRunnable(pkg, postedTime);
+        mFileWriteHandler.post(rnr);
+    }
+
     public void addNotification(final HistoricalNotification notification) {
         synchronized (mLock) {
             mBuffer.addNewNotificationToWrite(notification);
@@ -367,6 +372,49 @@
         }
     }
 
+    final class RemoveNotificationRunnable implements Runnable {
+        private String mPkg;
+        private long mPostedTime;
+        private NotificationHistory mNotificationHistory;
+
+        public RemoveNotificationRunnable(String pkg, long postedTime) {
+            mPkg = pkg;
+            mPostedTime = postedTime;
+        }
+
+        @VisibleForTesting
+        void setNotificationHistory(NotificationHistory nh) {
+            mNotificationHistory = nh;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "RemovePackageRunnable");
+            synchronized (mLock) {
+                // Remove from pending history
+                mBuffer.removeNotificationFromWrite(mPkg, mPostedTime);
+
+                Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
+                while (historyFileItr.hasNext()) {
+                    final AtomicFile af = historyFileItr.next();
+                    try {
+                        NotificationHistory notificationHistory = mNotificationHistory != null
+                                ? mNotificationHistory
+                                : new NotificationHistory();
+                        readLocked(af, notificationHistory,
+                                new NotificationHistoryFilter.Builder().build());
+                        if(notificationHistory.removeNotificationFromWrite(mPkg, mPostedTime)) {
+                            writeLocked(af, notificationHistory);
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Cannot clean up file on notification removal "
+                                + af.getBaseFile().getName(), e);
+                    }
+                }
+            }
+        }
+    }
+
     public static final class NotificationHistoryFileAttrProvider implements
             NotificationHistoryDatabase.FileAttrProvider {
         final static String TAG = "NotifHistoryFileDate";
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index 41bc29f..9aab0fd 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -130,7 +130,7 @@
         }
     }
 
-    public void onPackageRemoved(int userId, String packageName) {
+    public void onPackageRemoved(@UserIdInt int userId, String packageName) {
         synchronized (mLock) {
             if (!mUserUnlockedStates.get(userId, false)) {
                 if (mHistoryEnabled.get(userId, false)) {
@@ -150,6 +150,22 @@
         }
     }
 
+    public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
+        synchronized (mLock) {
+            int userId = UserHandle.getUserId(uid);
+            final NotificationHistoryDatabase userHistory =
+                    getUserHistoryAndInitializeIfNeededLocked(userId);
+            // TODO: it shouldn't be possible to delete a notification entry while the user is
+            // locked but we should handle it
+            if (userHistory == null) {
+                Slog.w(TAG, "Attempted to remove notif for locked/gone/disabled user "
+                        + userId);
+                return;
+            }
+            userHistory.deleteNotificationHistoryItem(pkg, postedTime);
+        }
+    }
+
     // TODO: wire this up to AMS when power button is long pressed
     public void triggerWriteToDisk() {
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f071135..ea77c36 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -193,6 +193,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.Condition;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.IStatusBarNotificationHolder;
@@ -2780,7 +2781,7 @@
                         if (!isSystemToast) {
                             int count = 0;
                             final int N = mToastQueue.size();
-                            for (int i=0; i<N; i++) {
+                            for (int i = 0; i < N; i++) {
                                 final ToastRecord r = mToastQueue.get(i);
                                 if (r.pkg.equals(pkg)) {
                                     count++;
@@ -2820,7 +2821,7 @@
 
             if (pkg == null || token == null) {
                 Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " token=" + token);
-                return ;
+                return;
             }
 
             synchronized (mToastQueue) {
@@ -2933,14 +2934,14 @@
 
         /**
          * Updates the enabled state for notifications for the given package (and uid).
-         * Additionally, this method marks the app importance as locked by the user, which means
+         * Additionally, this method marks the app importance as locked by the user, which
+         * means
          * that notifications from the app will <b>not</b> be considered for showing a
          * blocking helper.
          *
-         * @param pkg package that owns the notifications to update
-         * @param uid uid of the app providing notifications
+         * @param pkg     package that owns the notifications to update
+         * @param uid     uid of the app providing notifications
          * @param enabled whether notifications should be enabled for the app
-         *
          * @see #setNotificationsEnabledForPackage(String, int, boolean)
          */
         @Override
@@ -3031,6 +3032,12 @@
         }
 
         @Override
+        public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
+            checkCallerIsSystem();
+            mHistoryManager.deleteNotificationHistoryItem(pkg, uid, postedTime);
+        }
+
+        @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
             return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -3215,9 +3222,10 @@
 
         @Override
         public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
-                String channelId, boolean includeDeleted) {
+                String channelId, String conversationId, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mPreferencesHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
+            return mPreferencesHelper.getConversationNotificationChannel(
+                    pkg, uid, channelId, conversationId, true, includeDeleted);
         }
 
         @Override
@@ -3354,6 +3362,27 @@
         }
 
         @Override
+        public ParceledListSlice<ConversationChannelWrapper> getConversationsForPackage(String pkg,
+                int uid) {
+            enforceSystemOrSystemUI("getConversationsForPackage");
+            ArrayList<ConversationChannelWrapper> conversations =
+                    mPreferencesHelper.getConversations(pkg, uid);
+            for (ConversationChannelWrapper conversation : conversations) {
+                LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+                        .setPackage(pkg)
+                        .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+                        .setShortcutIds(Arrays.asList(
+                                conversation.getNotificationChannel().getConversationId()));
+                List<ShortcutInfo> shortcuts = mLauncherAppsService.getShortcuts(
+                        query, UserHandle.of(UserHandle.getUserId(uid)));
+                if (shortcuts != null && !shortcuts.isEmpty()) {
+                    conversation.setShortcutInfo(shortcuts.get(0));
+                }
+            }
+            return new ParceledListSlice<>(conversations);
+        }
+
+        @Override
         public NotificationChannelGroup getPopulatedNotificationChannelGroupForPackage(
                 String pkg, int uid, String groupId, boolean includeDeleted) {
             enforceSystemOrSystemUI("getPopulatedNotificationChannelGroupForPackage");
@@ -9256,7 +9285,7 @@
                 }
 
                 BackgroundThread.getHandler().post(() -> {
-                    if (hasCompanionDevice(serviceInfo)) {
+                    if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) {
                         notifyNotificationChannelChanged(
                                 serviceInfo, pkg, user, channel, modificationType);
                     }
@@ -9276,7 +9305,7 @@
                 }
 
                 BackgroundThread.getHandler().post(() -> {
-                    if (hasCompanionDevice(serviceInfo)) {
+                    if (serviceInfo.isSystem || hasCompanionDevice(serviceInfo)) {
                         notifyNotificationChannelGroupChanged(
                                 serviceInfo, pkg, user, group, modificationType);
                     }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index fe39322..a244c48 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -36,6 +36,7 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.RankingHelperProto;
 import android.text.TextUtils;
@@ -1176,6 +1177,43 @@
         return groups;
     }
 
+    public ArrayList<ConversationChannelWrapper> getConversations(String pkg, int uid) {
+        Objects.requireNonNull(pkg);
+        synchronized (mPackagePreferences) {
+            PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+            if (r == null) {
+                return new ArrayList<>();
+            }
+            ArrayList<ConversationChannelWrapper> conversations = new ArrayList<>();
+            int N = r.channels.size();
+            for (int i = 0; i < N; i++) {
+                final NotificationChannel nc = r.channels.valueAt(i);
+                if (!TextUtils.isEmpty(nc.getConversationId()) && !nc.isDeleted()) {
+                    ConversationChannelWrapper conversation = new ConversationChannelWrapper();
+                    conversation.setNotificationChannel(nc);
+                    conversation.setParentChannelLabel(
+                            r.channels.get(nc.getParentChannelId()).getName());
+                    boolean blockedByGroup = false;
+                    if (nc.getGroup() != null) {
+                        NotificationChannelGroup group = r.groups.get(nc.getGroup());
+                        if (group != null) {
+                            if (group.isBlocked()) {
+                                blockedByGroup = true;
+                            } else {
+                                conversation.setGroupLabel(group.getName());
+                            }
+                        }
+                    }
+                    if (!blockedByGroup) {
+                        conversations.add(conversation);
+                    }
+                }
+            }
+
+            return conversations;
+        }
+    }
+
     @Override
     public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
             boolean includeDeleted) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 42bc464..a440c62 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -36,6 +36,7 @@
 import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.Trace;
 import android.sysprop.ApexProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -46,6 +47,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.utils.TimingsTraceAndSlog;
 
 import com.google.android.collect.Lists;
 
@@ -375,8 +377,11 @@
 
         @Override
         public List<ActiveApexInfo> getActiveApexInfos() {
+            final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
+                    Trace.TRACE_TAG_APEX_MANAGER);
             synchronized (mLock) {
                 if (mActiveApexInfosCache == null) {
+                    t.traceBegin("getActiveApexInfos_noCache");
                     try {
                         mActiveApexInfosCache = new ArraySet<>();
                         final ApexInfo[] activePackages = mApexService.getActivePackages();
@@ -387,6 +392,7 @@
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Unable to retrieve packages from apexservice", e);
                     }
+                    t.traceEnd();
                 }
                 if (mActiveApexInfosCache != null) {
                     return new ArrayList<>(mActiveApexInfosCache);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 3ad1207..8c13b5b 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -34,6 +34,7 @@
 import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
 import android.content.pm.parsing.ComponentParseUtils.ParsedService;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Process;
 import android.os.Trace;
 import android.os.UserHandle;
@@ -171,11 +172,13 @@
         @Override
         public boolean packageIsEnabled(AndroidPackage pkg) {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "packageIsEnabled");
+            final long token = Binder.clearCallingIdentity();
             try {
                 // TODO(b/135203078): Do not use toAppInfo
                 return mInjector.getCompatibility().isChangeEnabled(
                         PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
             } finally {
+                Binder.restoreCallingIdentity(token);
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
         }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 12b9743..da07365 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -662,6 +662,23 @@
             }
         }
 
+        private void ensureStrictAccessShortcutsPermission(@NonNull String callingPackage) {
+            verifyCallingPackage(callingPackage);
+            if (!injectHasAccessShortcutsPermission(injectBinderCallingPid(),
+                    injectBinderCallingUid())) {
+                throw new SecurityException("Caller can't access shortcut information");
+            }
+        }
+
+        /**
+         * Returns true if the caller has the "ACCESS_SHORTCUTS" permission.
+         */
+        @VisibleForTesting
+        boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
+            return mContext.checkPermission(android.Manifest.permission.ACCESS_SHORTCUTS,
+                    callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
+        }
+
         @Override
         public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
                 String packageName, List shortcutIds, List<LocusId> locusIds,
@@ -711,6 +728,30 @@
         }
 
         @Override
+        public void cacheShortcuts(String callingPackage, String packageName, List<String> ids,
+                UserHandle targetUser) {
+            ensureStrictAccessShortcutsPermission(callingPackage);
+            if (!canAccessProfile(targetUser.getIdentifier(), "Cannot cache shortcuts")) {
+                return;
+            }
+
+            mShortcutServiceInternal.cacheShortcuts(getCallingUserId(),
+                    callingPackage, packageName, ids, targetUser.getIdentifier());
+        }
+
+        @Override
+        public void uncacheShortcuts(String callingPackage, String packageName, List<String> ids,
+                UserHandle targetUser) {
+            ensureStrictAccessShortcutsPermission(callingPackage);
+            if (!canAccessProfile(targetUser.getIdentifier(), "Cannot uncache shortcuts")) {
+                return;
+            }
+
+            mShortcutServiceInternal.uncacheShortcuts(getCallingUserId(),
+                    callingPackage, packageName, ids, targetUser.getIdentifier());
+        }
+
+        @Override
         public int getShortcutIconResId(String callingPackage, String packageName, String id,
                 int targetUserId) {
             ensureShortcutPermission(callingPackage);
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index e550bae..482fc49 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -344,18 +344,10 @@
                 int abi64 = PackageManager.NO_NATIVE_LIBRARIES;
                 if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                     if (extractLibs) {
-                        if (onIncremental) {
-                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
-                                    "incrementalNativeBinaries");
-                            abi32 = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
-                                    handle, nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
-                                    useIsaSpecificSubdirs);
-                        } else {
-                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
-                            abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
-                                    nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
-                                    useIsaSpecificSubdirs);
-                        }
+                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+                        abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                                nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS,
+                                useIsaSpecificSubdirs, onIncremental);
                     } else {
                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                         abi32 = NativeLibraryHelper.findSupportedAbi(
@@ -375,18 +367,10 @@
 
                 if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                     if (extractLibs) {
-                        if (onIncremental) {
-                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER,
-                                    "incrementalNativeBinaries");
-                            abi64 = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
-                                    handle, nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
-                                    useIsaSpecificSubdirs);
-                        } else {
-                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
-                            abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
-                                    nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
-                                    useIsaSpecificSubdirs);
-                        }
+                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+                        abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                                nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS,
+                                useIsaSpecificSubdirs, onIncremental);
                     } else {
                         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                         abi64 = NativeLibraryHelper.findSupportedAbi(
@@ -437,15 +421,9 @@
 
                 final int copyRet;
                 if (extractLibs) {
-                    if (onIncremental) {
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "incrementalNativeBinaries");
-                        copyRet = NativeLibraryHelper.configureNativeBinariesForSupportedAbi(pkg,
-                                handle, nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
-                    } else {
-                        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
-                        copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
-                                nativeLibraryRoot, abiList, useIsaSpecificSubdirs);
-                    }
+                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries");
+                    copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle,
+                            nativeLibraryRoot, abiList, useIsaSpecificSubdirs, onIncremental);
                 } else {
                     Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi");
                     copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 9116c40..33ef2d4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -129,6 +129,13 @@
     /** Upper bound on number of historical sessions for a UID */
     private static final long MAX_HISTORICAL_SESSIONS = 1048576;
 
+    /**
+     * Allow verification-skipping if it's a development app installed through ADB with
+     * disable verification flag specified.
+     */
+    private static final int ADB_DEV_MODE = PackageManager.INSTALL_FROM_ADB
+            | PackageManager.INSTALL_ALLOW_TEST;
+
     private final Context mContext;
     private final PackageManagerService mPm;
     private final ApexManager mApexManager;
@@ -531,8 +538,10 @@
             params.installFlags &= ~PackageManager.INSTALL_REQUEST_DOWNGRADE;
         }
 
-        if (callingUid != Process.SYSTEM_UID) {
-            // Only system_server can use INSTALL_DISABLE_VERIFICATION.
+        if (callingUid != Process.SYSTEM_UID
+                && (params.installFlags & ADB_DEV_MODE) != ADB_DEV_MODE) {
+            // Only system_server or tools under specific conditions (test app installed
+            // through ADB, and verification disabled flag specified) can disable verification.
             params.installFlags &= ~PackageManager.INSTALL_DISABLE_VERIFICATION;
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0cf8b42..944280d 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1681,10 +1681,7 @@
                 mInternalProgress = 0.5f;
                 computeProgressLocked(true);
 
-                // Unpack native libraries for non-incremental installation
-                if (!isIncrementalInstallation()) {
-                    extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
-                }
+                extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
             }
 
             // We've reached point of no return; call into PMS to install the stage.
@@ -2260,7 +2257,7 @@
         Slog.d(TAG, "Copied " + fromFiles.size() + " files into " + toDir);
     }
 
-    private static void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
+    private void extractNativeLibraries(File packageDir, String abiOverride, boolean inherit)
             throws PackageManagerException {
         final File libDir = new File(packageDir, NativeLibraryHelper.LIB_DIR_NAME);
         if (!inherit) {
@@ -2272,7 +2269,7 @@
         try {
             handle = NativeLibraryHelper.Handle.create(packageDir);
             final int res = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libDir,
-                    abiOverride);
+                    abiOverride, isIncrementalInstallation());
             if (res != PackageManager.INSTALL_SUCCEEDED) {
                 throw new PackageManagerException(res,
                         "Failed to extract native libraries, res=" + res);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6dd5ea2..2c85d06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3627,7 +3627,7 @@
             try {
                 handle = NativeLibraryHelper.Handle.create(dstCodePath);
                 ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
-                        null /*abiOverride*/);
+                        null /*abiOverride*/, false /*isIncremental*/);
             } catch (IOException e) {
                 logCriticalInfo(Log.ERROR, "Failed to extract native libraries"
                         + "; pkg: " + packageName);
@@ -13353,42 +13353,53 @@
      *
      * @return true if verification should be performed
      */
-    private boolean isVerificationEnabled(int userId, int installFlags, int installerUid) {
+    private boolean isVerificationEnabled(
+            PackageInfoLite pkgInfoLite, int userId, int installFlags, int installerUid) {
         if (!DEFAULT_VERIFY_ENABLE) {
             return false;
         }
 
-        if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
-            return false;
-        }
-
         // Check if installing from ADB
         if ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0) {
             if (isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
                 return true;
             }
-            // Check if the developer does not want package verification for ADB installs
+            // Check if the developer wants to skip verification for ADB installs
+            if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+                synchronized (mLock) {
+                    if (mSettings.mPackages.get(pkgInfoLite.packageName) == null) {
+                        // Always verify fresh install
+                        return true;
+                    }
+                }
+                // Only skip when apk is debuggable
+                return !pkgInfoLite.debuggable;
+            }
             return Global.getInt(mContext.getContentResolver(),
                     Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
-        } else {
-            // only when not installed from ADB, skip verification for instant apps when
-            // the installer and verifier are the same.
-            if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
-                if (mInstantAppInstallerActivity != null
-                        && mInstantAppInstallerActivity.packageName.equals(
-                                mRequiredVerifierPackage)) {
-                    try {
-                        mInjector.getAppOpsManager()
-                                .checkPackage(installerUid, mRequiredVerifierPackage);
-                        if (DEBUG_VERIFY) {
-                            Slog.i(TAG, "disable verification for instant app");
-                        }
-                        return false;
-                    } catch (SecurityException ignore) { }
-                }
-            }
-            return true;
         }
+
+        if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+            return false;
+        }
+
+        // only when not installed from ADB, skip verification for instant apps when
+        // the installer and verifier are the same.
+        if ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) {
+            if (mInstantAppInstallerActivity != null
+                    && mInstantAppInstallerActivity.packageName.equals(
+                            mRequiredVerifierPackage)) {
+                try {
+                    mInjector.getAppOpsManager()
+                            .checkPackage(installerUid, mRequiredVerifierPackage);
+                    if (DEBUG_VERIFY) {
+                        Slog.i(TAG, "disable verification for instant app");
+                    }
+                    return false;
+                } catch (SecurityException ignore) { }
+            }
+        }
+        return true;
     }
 
     /**
@@ -14552,7 +14563,7 @@
                     verificationInfo == null ? -1 : verificationInfo.installerUid;
             if (!origin.existing && requiredUid != -1
                     && isVerificationEnabled(
-                    verifierUser.getIdentifier(), installFlags, installerUid)) {
+                            pkgLite, verifierUser.getIdentifier(), installFlags, installerUid)) {
                 final Intent verification = new Intent(
                         Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                 verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -14962,12 +14973,13 @@
                 return ret;
             }
 
+            final boolean isIncremental = isIncrementalPath(codeFile.getAbsolutePath());
             final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
             NativeLibraryHelper.Handle handle = null;
             try {
                 handle = NativeLibraryHelper.Handle.create(codeFile);
                 ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
-                        abiOverride);
+                        abiOverride, isIncremental);
             } catch (IOException e) {
                 Slog.e(TAG, "Copying native libraries failed", e);
                 ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 71a5545..9395c97 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -794,6 +794,7 @@
         ret.verifiers = pkg.verifiers;
         ret.recommendedInstallLocation = recommendedInstallLocation;
         ret.multiArch = pkg.multiArch;
+        ret.debuggable = pkg.debuggable;
 
         return ret;
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index bb69680..cb94043 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2757,6 +2757,9 @@
                 case "--no-wait":
                     params.mWaitForStagedSessionReady = false;
                     break;
+                case "--skip-verification":
+                    sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index d16c074..377fd16 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1704,7 +1704,7 @@
             ShortcutInfo.validateIcon(shortcut.getIcon());
         }
 
-        shortcut.replaceFlags(0);
+        shortcut.replaceFlags(shortcut.getFlags() & ShortcutInfo.FLAG_LONG_LIVED);
     }
 
     private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate) {
@@ -2758,6 +2758,68 @@
         }
 
         @Override
+        public void cacheShortcuts(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId) {
+            updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds,
+                    userId, /* doCache= */ true);
+        }
+
+        @Override
+        public void uncacheShortcuts(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId) {
+            updateCachedShortcutsInternal(launcherUserId, callingPackage, packageName, shortcutIds,
+                    userId, /* doCache= */ false);
+        }
+
+        private void updateCachedShortcutsInternal(int launcherUserId,
+                @NonNull String callingPackage, @NonNull String packageName,
+                @NonNull List<String> shortcutIds, int userId, boolean doCache) {
+            // Calling permission must be checked by LauncherAppsImpl.
+            Preconditions.checkStringNotEmpty(packageName, "packageName");
+            Objects.requireNonNull(shortcutIds, "shortcutIds");
+
+            synchronized (mLock) {
+                throwIfUserLockedL(userId);
+                throwIfUserLockedL(launcherUserId);
+
+                final int idSize = shortcutIds.size();
+                final ShortcutPackage sp = getUserShortcutsLocked(userId)
+                        .getPackageShortcutsIfExists(packageName);
+                if (idSize == 0 || sp == null) {
+                    return;
+                }
+
+                for (int i = 0; i < idSize; i++) {
+                    final String id = Preconditions.checkStringNotEmpty(shortcutIds.get(i));
+                    final ShortcutInfo si = sp.findShortcutById(id);
+                    if (si == null || doCache == si.isCached()) {
+                        continue;
+                    }
+
+                    if (doCache) {
+                        if (si.isDynamic() && si.isLongLived()) {
+                            si.addFlags(ShortcutInfo.FLAG_CACHED);
+                        } else {
+                            Log.w(TAG, "Only dynamic long lived shortcuts can get cached. Ignoring"
+                                    + "shortcut " + si.getId());
+                        }
+                    } else {
+                        if (si.isDynamic()) {
+                            si.clearFlags(ShortcutInfo.FLAG_CACHED);
+                        } else {
+                            sp.deleteLongLivedWithId(id, /*ignoreInvisible=*/ true);
+                        }
+                    }
+                }
+            }
+            packageShortcutsChanged(packageName, userId);
+
+            verifyStates();
+        }
+
+        @Override
         public Intent[] createShortcutIntents(int launcherUserId,
                 @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId,
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8935453..614cc3f 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -57,6 +57,7 @@
 import android.os.UserManagerInternal;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
+import android.text.TextUtils;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -67,6 +68,7 @@
 import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
+import com.android.server.rollback.WatchdogRollbackLogger;
 
 import java.io.File;
 import java.io.IOException;
@@ -99,6 +101,10 @@
     @GuardedBy("mStagedSessions")
     private final SparseIntArray mSessionRollbackIds = new SparseIntArray();
 
+    @GuardedBy("mFailedPackageNames")
+    private final List<String> mFailedPackageNames = new ArrayList<>();
+    private String mNativeFailureReason;
+
     StagingManager(PackageInstallerService pi, Context context) {
         mPi = pi;
         mContext = context;
@@ -441,6 +447,22 @@
         }
     }
 
+    /**
+     *  Prepares for the logging of apexd reverts by storing the native failure reason if necessary,
+     *  and adding the package name of the session which apexd reverted to the list of reverted
+     *  session package names.
+     *  Logging needs to wait until the ACTION_BOOT_COMPLETED broadcast is sent.
+     */
+    private void prepareForLoggingApexdRevert(@NonNull PackageInstallerSession session,
+            @NonNull String nativeFailureReason) {
+        synchronized (mFailedPackageNames) {
+            mNativeFailureReason = nativeFailureReason;
+            if (session.getPackageName() != null) {
+                mFailedPackageNames.add(session.getPackageName());
+            }
+        }
+    }
+
     private void resumeSession(@NonNull PackageInstallerSession session) {
         Slog.d(TAG, "Resuming session " + session.sessionId);
 
@@ -450,6 +472,12 @@
             // Check with apexservice whether the apex packages have been activated.
             apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
 
+            // Prepare for logging a native crash during boot, if one occurred.
+            if (apexSessionInfo != null && !TextUtils.isEmpty(
+                    apexSessionInfo.crashingNativeProcess)) {
+                prepareForLoggingApexdRevert(session, apexSessionInfo.crashingNativeProcess);
+            }
+
             if (apexSessionInfo != null && apexSessionInfo.isVerified) {
                 // Session has been previously submitted to apexd, but didn't complete all the
                 // pre-reboot verification, perhaps because the device rebooted in the meantime.
@@ -955,12 +983,23 @@
         }
     }
 
+    private void logFailedApexSessionsIfNecessary() {
+        synchronized (mFailedPackageNames) {
+            if (!mFailedPackageNames.isEmpty()) {
+                WatchdogRollbackLogger.logApexdRevert(mContext,
+                        mFailedPackageNames, mNativeFailureReason);
+            }
+        }
+    }
+
     void systemReady() {
         // Register the receiver of boot completed intent for staging manager.
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context ctx, Intent intent) {
                 mPreRebootVerificationHandler.readyToStart();
+                BackgroundThread.getExecutor().execute(
+                        () -> logFailedApexSessionsIfNecessary());
                 ctx.unregisterReceiver(this);
             }
         }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0cb8f495..1c02161 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1901,7 +1901,7 @@
 
     @Override
     public boolean hasBaseUserRestriction(String restrictionKey, @UserIdInt int userId) {
-        checkManageUsersPermission("hasBaseUserRestriction");
+        checkManageOrCreateUsersPermission("hasBaseUserRestriction");
         if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
             return false;
         }
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 2c7795a..43d4596 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -39,9 +39,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageManagerInternal.PackageListObserver;
 import android.content.pm.PermissionInfo;
-import android.content.pm.parsing.AndroidPackage;
 import android.os.Build;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -52,14 +50,15 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LongSparseLongArray;
-import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.infra.AndroidFuture;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IntPair;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.FgThread;
@@ -70,6 +69,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ExecutionException;
 
 /**
@@ -100,7 +100,7 @@
      * scheduled for a package/user.
      */
     @GuardedBy("mLock")
-    private final ArraySet<Pair<String, Integer>> mIsPackageSyncsScheduled = new ArraySet<>();
+    private final ArraySet<Integer> mIsPackageSyncsScheduled = new ArraySet<>();
 
     public PermissionPolicyService(@NonNull Context context) {
         super(context);
@@ -125,10 +125,8 @@
 
             @Override
             public void onPackageChanged(String packageName, int uid) {
-                final int userId = UserHandle.getUserId(uid);
-
-                if (isStarted(userId)) {
-                    synchronizePackagePermissionsAndAppOpsForUser(packageName, userId);
+                if (isStarted(UserHandle.getUserId(uid))) {
+                    synchronizePackagePermissionsAndAppOpsForUser(uid);
                 }
             }
 
@@ -139,12 +137,21 @@
         });
 
         permManagerInternal.addOnRuntimePermissionStateChangedListener(
-                this::synchronizePackagePermissionsAndAppOpsAsyncForUser);
+                (packageName, userId) -> {
+                    int uid;
+                    try {
+                        uid = getContext().getPackageManager().getPackageUidAsUser(packageName, 0,
+                                userId);
+                    } catch (NameNotFoundException e) {
+                        Slog.e(LOG_TAG, "Cannot synchronize changed package " + packageName, e);
+                        return;
+                    }
+                    synchronizeUidPermissionsAndAppOpsAsync(uid);
+                });
 
         mAppOpsCallback = new IAppOpsCallback.Stub() {
             public void opChanged(int op, int uid, String packageName) {
-                synchronizePackagePermissionsAndAppOpsAsyncForUser(packageName,
-                        UserHandle.getUserId(uid));
+                synchronizeUidPermissionsAndAppOpsAsync(uid);
             }
         };
 
@@ -194,19 +201,17 @@
         return AppOpsManager.opToSwitch(op);
     }
 
-    private void synchronizePackagePermissionsAndAppOpsAsyncForUser(@NonNull String packageName,
-            @UserIdInt int changedUserId) {
-        if (isStarted(changedUserId)) {
+    private void synchronizeUidPermissionsAndAppOpsAsync(int uid) {
+        if (isStarted(UserHandle.getUserId(uid))) {
             synchronized (mLock) {
-                if (mIsPackageSyncsScheduled.add(new Pair<>(packageName, changedUserId))) {
+                if (mIsPackageSyncsScheduled.add(uid)) {
                     FgThread.getHandler().sendMessage(PooledLambda.obtainMessage(
                             PermissionPolicyService
                                     ::synchronizePackagePermissionsAndAppOpsForUser,
-                            this, packageName, changedUserId));
+                            this, uid));
                 } else {
                     if (DEBUG) {
-                        Slog.v(LOG_TAG, "sync for " + packageName + "/" + changedUserId
-                                + " already scheduled");
+                        Slog.v(LOG_TAG, "sync for " + uid + " already scheduled");
                     }
                 }
             }
@@ -335,39 +340,20 @@
     /**
      * Synchronize a single package.
      */
-    private void synchronizePackagePermissionsAndAppOpsForUser(@NonNull String packageName,
-            @UserIdInt int userId) {
+    private void synchronizePackagePermissionsAndAppOpsForUser(int uid) {
         synchronized (mLock) {
-            mIsPackageSyncsScheduled.remove(new Pair<>(packageName, userId));
+            mIsPackageSyncsScheduled.remove(uid);
         }
 
         if (DEBUG) {
             Slog.v(LOG_TAG,
-                    "synchronizePackagePermissionsAndAppOpsForUser(" + packageName + ", "
-                            + userId + ")");
+                    "synchronizePackagePermissionsAndAppOpsForUser(" + uid + ")");
         }
 
-        final PackageManagerInternal packageManagerInternal = LocalServices.getService(
-                PackageManagerInternal.class);
-        final PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName, 0,
-                Process.SYSTEM_UID, userId);
-        if (pkg == null) {
-            return;
-        }
         final PermissionToOpSynchroniser synchroniser = new PermissionToOpSynchroniser(
-                getUserContext(getContext(), UserHandle.of(userId)));
-        synchroniser.addPackage(pkg.packageName);
-        final String[] sharedPkgNames = packageManagerInternal.getSharedUserPackagesForPackage(
-                pkg.packageName, userId);
-
-        for (String sharedPkgName : sharedPkgNames) {
-            final AndroidPackage sharedPkg = packageManagerInternal
-                    .getPackage(sharedPkgName);
-            if (sharedPkg != null) {
-                synchroniser.addPackage(sharedPkg.getPackageName());
-            }
-        }
-        synchroniser.syncPackages();
+                getUserContext(getContext(), UserHandle.getUserHandleForUid(uid)));
+        synchroniser.addUid(uid);
+        synchroniser.syncUids();
     }
 
     /**
@@ -381,8 +367,8 @@
         final PermissionToOpSynchroniser synchronizer = new PermissionToOpSynchroniser(
                 getUserContext(getContext(), UserHandle.of(userId)));
         packageManagerInternal.forEachPackage(
-                (pkg) -> synchronizer.addPackage(pkg.getPackageName()));
-        synchronizer.syncPackages();
+                (pkg) -> synchronizer.addUid(pkg.getUid()));
+        synchronizer.syncUids();
     }
 
     /**
@@ -397,37 +383,51 @@
 
         private final @NonNull ArrayMap<String, PermissionInfo> mRuntimePermissionInfos;
 
+        // Cache uid -> packageNames
+        private SparseArray<String[]> mUidToPkg = new SparseArray<>();
+
         /**
          * All ops that need to be flipped to allow.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToAllow = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToAllow = new ArraySet<>();
 
         /**
          * All ops that need to be flipped to ignore.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToIgnore = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToIgnore = new ArraySet<>();
 
         /**
          * All ops that need to be flipped to ignore if not allowed.
          *
          * Currently, only used by soft restricted permissions logic.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToIgnoreIfNotAllowed = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToIgnoreIfNotAllowed = new ArraySet<>();
 
         /**
          * All ops that need to be flipped to foreground.
          *
          * Currently, only used by the foreground/background permissions logic.
          *
-         * @see #syncPackages
+         * @see #syncUids
          */
-        private final @NonNull ArrayList<OpToChange> mOpsToForeground = new ArrayList<>();
+        private final @NonNull ArraySet<OpToChange> mOpsToForeground = new ArraySet<>();
+
+        private @Nullable String[] getPackageNamesForUid(int uid) {
+            String[] pkgs = mUidToPkg.get(uid);
+            if (pkgs != null) {
+                return pkgs;
+            }
+
+            pkgs = mPackageManager.getPackagesForUid(uid);
+            mUidToPkg.put(uid, pkgs);
+            return pkgs;
+        }
 
         PermissionToOpSynchroniser(@NonNull Context context) {
             mContext = context;
@@ -449,11 +449,11 @@
         }
 
         /**
-         * Set app ops that were added in {@link #addPackage}.
+         * Set app ops that were added in {@link #addUid}.
          *
          * <p>This processes ops previously added by {@link #addAppOps(PackageInfo, String)}
          */
-        private void syncPackages() {
+        private void syncUids() {
             // Remember which ops were already set. This makes sure that we always set the most
             // permissive mode if two OpChanges are scheduled. This can e.g. happen if two
             // permissions change the same op. See {@link #getSwitchOp}.
@@ -461,42 +461,42 @@
 
             final int allowCount = mOpsToAllow.size();
             for (int i = 0; i < allowCount; i++) {
-                final OpToChange op = mOpsToAllow.get(i);
+                final OpToChange op = mOpsToAllow.valueAt(i);
 
-                setUidModeAllowed(op.code, op.uid, op.packageName);
+                setUidModeAllowed(op.code, op.uid);
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
             final int foregroundCount = mOpsToForeground.size();
             for (int i = 0; i < foregroundCount; i++) {
-                final OpToChange op = mOpsToForeground.get(i);
+                final OpToChange op = mOpsToForeground.valueAt(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                setUidModeForeground(op.code, op.uid, op.packageName);
+                setUidModeForeground(op.code, op.uid);
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
             final int ignoreCount = mOpsToIgnore.size();
             for (int i = 0; i < ignoreCount; i++) {
-                final OpToChange op = mOpsToIgnore.get(i);
+                final OpToChange op = mOpsToIgnore.valueAt(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                setUidModeIgnored(op.code, op.uid, op.packageName);
+                setUidModeIgnored(op.code, op.uid);
                 alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
             }
 
             final int ignoreIfNotAllowedCount = mOpsToIgnoreIfNotAllowed.size();
             for (int i = 0; i < ignoreIfNotAllowedCount; i++) {
-                final OpToChange op = mOpsToIgnoreIfNotAllowed.get(i);
+                final OpToChange op = mOpsToIgnoreIfNotAllowed.valueAt(i);
                 if (alreadySetAppOps.indexOfKey(IntPair.of(op.uid, op.code)) >= 0) {
                     continue;
                 }
 
-                boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid, op.packageName);
+                boolean wasSet = setUidModeIgnoredIfNotAllowed(op.code, op.uid);
                 if (wasSet) {
                     alreadySetAppOps.put(IntPair.of(op.uid, op.code), 1);
                 }
@@ -555,7 +555,7 @@
             }
 
             int uid = packageInfo.applicationInfo.uid;
-            OpToChange opToChange = new OpToChange(uid, packageName, appOpCode);
+            OpToChange opToChange = new OpToChange(uid, appOpCode);
             switch (appOpMode) {
                 case MODE_ALLOWED:
                     mOpsToAllow.add(opToChange);
@@ -618,8 +618,7 @@
             }
 
             int uid = packageInfo.applicationInfo.uid;
-            String packageName = packageInfo.packageName;
-            OpToChange extraOpToChange = new OpToChange(uid, packageName, extraOpCode);
+            OpToChange extraOpToChange = new OpToChange(uid, extraOpCode);
             if (policy.mayAllowExtraAppOp()) {
                 mOpsToAllow.add(extraOpToChange);
             } else {
@@ -632,45 +631,56 @@
         }
 
         /**
-         * Add a package for {@link #syncPackages() processing} later.
+         * Add a Uid for {@link #syncUids() processing} later.
          *
          * <p>Note: Called with the package lock held. Do <u>not</u> call into app-op manager.
          *
-         * @param pkgName The package to add for later processing.
+         * @param uid The uid to add for later processing.
          */
-        void addPackage(@NonNull String pkgName) {
-            final PackageInfo pkg;
-            try {
-                pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
-            } catch (NameNotFoundException e) {
+        void addUid(int uid) {
+            String[] pkgNames = getPackageNamesForUid(uid);
+            if (pkgNames == null) {
                 return;
             }
 
-            if (pkg.requestedPermissions == null) {
-                return;
-            }
+            for (String pkgName : pkgNames) {
+                final PackageInfo pkg;
+                try {
+                    pkg = mPackageManager.getPackageInfo(pkgName, GET_PERMISSIONS);
+                } catch (NameNotFoundException e) {
+                    continue;
+                }
 
-            for (String permission : pkg.requestedPermissions) {
-                addAppOps(pkg, permission);
+                if (pkg.requestedPermissions == null) {
+                    continue;
+                }
+
+                for (String permission : pkg.requestedPermissions) {
+                    addAppOps(pkg, permission);
+                }
             }
         }
 
-        private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) {
-            setUidMode(opCode, uid, MODE_ALLOWED, packageName);
+        private void setUidModeAllowed(int opCode, int uid) {
+            setUidMode(opCode, uid, MODE_ALLOWED);
         }
 
-        private void setUidModeForeground(int opCode, int uid, @NonNull String packageName) {
-            setUidMode(opCode, uid, MODE_FOREGROUND, packageName);
+        private void setUidModeForeground(int opCode, int uid) {
+            setUidMode(opCode, uid, MODE_FOREGROUND);
         }
 
-        private void setUidModeIgnored(int opCode, int uid, @NonNull String packageName) {
-            setUidMode(opCode, uid, MODE_IGNORED, packageName);
+        private void setUidModeIgnored(int opCode, int uid) {
+            setUidMode(opCode, uid, MODE_IGNORED);
         }
 
-        private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid,
-                @NonNull String packageName) {
+        private boolean setUidModeIgnoredIfNotAllowed(int opCode, int uid) {
+            String[] pkgsOfUid = getPackageNamesForUid(uid);
+            if (ArrayUtils.isEmpty(pkgsOfUid)) {
+                return false;
+            }
+
             final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
-                    opCode), uid, packageName);
+                    opCode), uid, pkgsOfUid[0]);
             if (currentMode != MODE_ALLOWED) {
                 if (currentMode != MODE_IGNORED) {
                     mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, MODE_IGNORED,
@@ -681,20 +691,24 @@
             return false;
         }
 
-        private void setUidMode(int opCode, int uid, int mode,
-                @NonNull String packageName) {
+        private void setUidMode(int opCode, int uid, int mode) {
+            String[] pkgsOfUid = getPackageNamesForUid(uid);
+            if (ArrayUtils.isEmpty(pkgsOfUid)) {
+                return;
+            }
+
             final int oldMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
-                    opCode), uid, packageName);
+                    opCode), uid, pkgsOfUid[0]);
             if (oldMode != mode) {
                 mAppOpsManagerInternal.setUidModeIgnoringCallback(opCode, uid, mode,
                         mAppOpsCallback);
                 final int newMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager.opToPublicName(
-                        opCode), uid, packageName);
+                        opCode), uid, pkgsOfUid[0]);
                 if (newMode != mode) {
                     // Work around incorrectly-set package mode. It never makes sense for app ops
                     // related to runtime permissions, but can get in the way and we have to reset
                     // it.
-                    mAppOpsManagerInternal.setModeIgnoringCallback(opCode, uid, packageName,
+                    mAppOpsManagerInternal.setModeIgnoringCallback(opCode, uid, pkgsOfUid[0],
                             AppOpsManager.opToDefaultMode(opCode), mAppOpsCallback);
                 }
             }
@@ -702,14 +716,30 @@
 
         private class OpToChange {
             final int uid;
-            final @NonNull String packageName;
             final int code;
 
-            OpToChange(int uid, @NonNull String packageName, int code) {
+            OpToChange(int uid, int code) {
                 this.uid = uid;
-                this.packageName = packageName;
                 this.code = code;
             }
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) {
+                    return true;
+                }
+                if (o == null || getClass() != o.getClass()) {
+                    return false;
+                }
+
+                OpToChange other = (OpToChange) o;
+                return uid == other.uid && code == other.code;
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(uid, code);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 4d7af9c..b5da1c2 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -182,6 +182,15 @@
     private int mNumPackageSessionsWithSuccess;
 
     /**
+     * A temp flag to facilitate merging of the 2 rollback collections managed by
+     * RollbackManagerServiceImpl. True if this rollback is in the process of enabling and was
+     * originally managed by RollbackManagerServiceImpl#mNewRollbacks.
+     * TODO: remove this flag when merge is completed.
+     */
+    @GuardedBy("mLock")
+    private boolean mIsNewRollback = false;
+
+    /**
      * Constructs a new, empty Rollback instance.
      *
      * @param rollbackId the id of the rollback.
@@ -829,6 +838,18 @@
         }
     }
 
+    void setIsNewRollback(boolean newRollback) {
+        synchronized (mLock) {
+            mIsNewRollback = newRollback;
+        }
+    }
+
+    boolean isNewRollback() {
+        synchronized (mLock) {
+            return mIsNewRollback;
+        }
+    }
+
     static String rollbackStateToString(@RollbackState int state) {
         switch (state) {
             case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 8bd9533..1421258 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -19,6 +19,7 @@
 import android.Manifest;
 import android.annotation.AnyThread;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.AppOpsManager;
@@ -50,7 +51,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
-import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.Log;
 import android.util.LongArrayQueue;
@@ -121,10 +121,6 @@
     @GuardedBy("mLock")
     private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
 
-    // Rollbacks we are in the process of enabling.
-    @GuardedBy("mLock")
-    private final Set<Rollback> mNewRollbacks = new ArraySet<>();
-
     // The list of all rollbacks, including available and committed rollbacks.
     @GuardedBy("mLock")
     private final List<Rollback> mRollbacks;
@@ -240,17 +236,14 @@
                         Slog.v(TAG, "broadcast=ACTION_CANCEL_ENABLE_ROLLBACK token=" + token);
                     }
                     synchronized (mLock) {
-                        Rollback found = null;
-                        for (Rollback newRollback : mNewRollbacks) {
-                            if (newRollback.hasToken(token)) {
-                                found = newRollback;
+                        for (int i = 0; i < mRollbacks.size(); ++i) {
+                            Rollback rollback = mRollbacks.get(i);
+                            if (rollback.hasToken(token) && rollback.isEnabling()) {
+                                mRollbacks.remove(i);
+                                rollback.delete(mAppDataRollbackHelper);
                                 break;
                             }
                         }
-                        if (found != null) {
-                            mNewRollbacks.remove(found);
-                            found.delete(mAppDataRollbackHelper);
-                        }
                     }
                 }
             }
@@ -442,15 +435,6 @@
                     rollback.delete(mAppDataRollbackHelper);
                 }
             }
-            Iterator<Rollback> iter2 = mNewRollbacks.iterator();
-            while (iter2.hasNext()) {
-                Rollback newRollback = iter2.next();
-                if (newRollback.includesPackage(packageName)) {
-                    iter2.remove();
-                    newRollback.delete(mAppDataRollbackHelper);
-                }
-
-            }
         }
     }
 
@@ -810,7 +794,8 @@
             newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
             if (newRollback == null) {
                 newRollback = createNewRollbackLocked(parentSession);
-                mNewRollbacks.add(newRollback);
+                mRollbacks.add(newRollback);
+                newRollback.setIsNewRollback(true);
             }
         }
         newRollback.addToken(token);
@@ -818,34 +803,6 @@
         return enableRollbackForPackageSession(newRollback, packageSession);
     }
 
-    @WorkerThread
-    private void removeRollbackForPackageSessionId(int sessionId) {
-        if (LOCAL_LOGV) {
-            Slog.v(TAG, "removeRollbackForPackageSessionId=" + sessionId);
-        }
-
-        synchronized (mLock) {
-            Rollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
-            if (newRollback != null) {
-                Slog.w(TAG, "Delete new rollback id=" + newRollback.info.getRollbackId()
-                        + " for session id=" + sessionId);
-                mNewRollbacks.remove(newRollback);
-                newRollback.delete(mAppDataRollbackHelper);
-            }
-            Iterator<Rollback> iter = mRollbacks.iterator();
-            while (iter.hasNext()) {
-                Rollback rollback = iter.next();
-                if (rollback.getStagedSessionId() == sessionId) {
-                    Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
-                            + " for session id=" + sessionId);
-                    iter.remove();
-                    rollback.delete(mAppDataRollbackHelper);
-                    break;
-                }
-            }
-        }
-    }
-
     /**
      * Do code and userdata backups to enable rollback of the given session.
      * In case of multiPackage sessions, <code>session</code> should be one of
@@ -966,15 +923,10 @@
                     + " users=" + Arrays.toString(userIds));
         }
         synchronized (mLock) {
-            // staged installs
             for (int i = 0; i < mRollbacks.size(); i++) {
                 Rollback rollback = mRollbacks.get(i);
                 rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
             }
-            // non-staged installs
-            for (Rollback rollback : mNewRollbacks) {
-                rollback.snapshotUserData(packageName, userIds, mAppDataRollbackHelper);
-            }
         }
     }
 
@@ -1200,7 +1152,8 @@
                 synchronized (mLock) {
                     newRollback = getNewRollbackForPackageSessionLocked(sessionId);
                     if (newRollback != null && newRollback.notifySessionWithSuccess()) {
-                        mNewRollbacks.remove(newRollback);
+                        mRollbacks.remove(newRollback);
+                        newRollback.setIsNewRollback(false);
                     } else {
                         // Not all child sessions finished with success.
                         // Don't enable the rollback yet.
@@ -1215,7 +1168,15 @@
                     }
                 }
             } else {
-                removeRollbackForPackageSessionId(sessionId);
+                synchronized (mLock) {
+                    Rollback rollback = getRollbackForSessionLocked(sessionId);
+                    if (rollback != null && rollback.isEnabling()) {
+                        Slog.w(TAG, "Delete rollback id=" + rollback.info.getRollbackId()
+                                + " for failed session id=" + sessionId);
+                        mRollbacks.remove(rollback);
+                        rollback.delete(mAppDataRollbackHelper);
+                    }
+                }
             }
         }
     }
@@ -1376,6 +1337,25 @@
     }
 
     /**
+     * Returns the Rollback associated with the given session if parent or child session id matches.
+     * Returns null if not found.
+     */
+    @WorkerThread
+    @GuardedBy("mLock")
+    @Nullable
+    private Rollback getRollbackForSessionLocked(int sessionId) {
+        // We expect mRollbacks to be a very small list; linear search should be plenty fast.
+        for (int i = 0; i < mRollbacks.size(); ++i) {
+            Rollback rollback = mRollbacks.get(i);
+            if (rollback.getStagedSessionId() == sessionId
+                    || rollback.containsSessionId(sessionId)) {
+                return rollback;
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the NewRollback associated with the given package session.
      * Returns null if no NewRollback is found for the given package
      * session.
@@ -1383,11 +1363,11 @@
     @WorkerThread
     @GuardedBy("mLock")
     Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
-        // We expect mNewRollbacks to be a very small list; linear search
+        // We expect mRollbacks to be a very small list; linear search
         // should be plenty fast.
-        for (Rollback newRollback: mNewRollbacks) {
-            if (newRollback.containsSessionId(packageSessionId)) {
-                return newRollback;
+        for (Rollback rollback: mRollbacks) {
+            if (rollback.isNewRollback() && rollback.containsSessionId(packageSessionId)) {
+                return rollback;
             }
         }
         return null;
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index 46ec2f8..1be6f22 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -20,7 +20,12 @@
 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING;
 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK;
 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT;
 import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE;
+import static com.android.internal.util.FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,7 +38,6 @@
 import android.content.rollback.RollbackInfo;
 import android.util.ArraySet;
 import android.util.Slog;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FrameworkStatsLog;
@@ -58,8 +62,8 @@
     private static String getLoggingParentName(Context context, @NonNull String packageName) {
         PackageManager packageManager = context.getPackageManager();
         try {
-            ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
-                    PackageManager.GET_META_DATA);
+            int flags = PackageManager.MATCH_APEX | PackageManager.GET_META_DATA;
+            ApplicationInfo ai = packageManager.getPackageInfo(packageName, flags).applicationInfo;
             if (ai.metaData == null) {
                 return null;
             }
@@ -95,6 +99,22 @@
         return loggingParent;
     }
 
+
+    /**
+     * Gets the set of parent packages for a given set of failed package names. In the case that
+     * multiple sessions have failed, we want to log failure for each of the parent packages.
+     * Even if multiple failed packages have the same parent, we only log the parent package once.
+     */
+    private static Set<VersionedPackage> getLogPackages(Context context,
+            @NonNull List<String> failedPackageNames) {
+        Set<VersionedPackage> parentPackages = new ArraySet<>();
+        for (String failedPackageName: failedPackageNames) {
+            parentPackages.add(getLogPackage(context, new VersionedPackage(failedPackageName, 0)));
+        }
+        return parentPackages;
+    }
+
+
     static void logRollbackStatusOnBoot(Context context, int rollbackId,
             List<RollbackInfo> recentlyCommittedRollbacks) {
         PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
@@ -142,19 +162,36 @@
         for (VersionedPackage oldLoggingPackage : oldLoggingPackages) {
             if (sessionInfo.isStagedSessionApplied()) {
                 logEvent(oldLoggingPackage,
-                        FrameworkStatsLog
-                                .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
                         WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
             } else if (sessionInfo.isStagedSessionFailed()) {
                 logEvent(oldLoggingPackage,
-                        FrameworkStatsLog
-                                .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+                        WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
                         WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_UNKNOWN, "");
             }
         }
     }
 
     /**
+     * Logs that one or more apexd reverts have occurred, along with the crashing native process
+     * that caused apexd to revert during boot.
+     *
+     * @param context the context to use when determining the log packages
+     * @param failedPackageNames a list of names of packages which were reverted
+     * @param failingNativeProcess the crashing native process which caused a revert
+     */
+    public static void logApexdRevert(Context context, @NonNull List<String> failedPackageNames,
+            @NonNull String failingNativeProcess) {
+        Set<VersionedPackage> logPackages = getLogPackages(context, failedPackageNames);
+        for (VersionedPackage logPackage: logPackages) {
+            logEvent(logPackage,
+                    WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                    WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT,
+                    failingNativeProcess);
+        }
+    }
+
+    /**
      * Log a Watchdog rollback event to statsd.
      *
      * @param logPackage the package to associate the rollback with.
@@ -169,12 +206,25 @@
                 + " rollbackReason: " + rollbackReasonToString(rollbackReason)
                 + " failedPackageName: " + failingPackageName);
         if (logPackage != null) {
-            StatsLog.logWatchdogRollbackOccurred(type, logPackage.getPackageName(),
-                    logPackage.getVersionCode(), rollbackReason, failingPackageName);
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                    type,
+                    logPackage.getPackageName(),
+                    logPackage.getVersionCode(),
+                    rollbackReason,
+                    failingPackageName,
+                    new byte[]{});
         } else {
             // In the case that the log package is null, still log an empty string as an
             // indication that retrieving the logging parent failed.
-            StatsLog.logWatchdogRollbackOccurred(type, "", 0, rollbackReason, failingPackageName);
+            FrameworkStatsLog.write(
+                    FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+                    type,
+                    "",
+                    0,
+                    rollbackReason,
+                    failingPackageName,
+                    new byte[]{});
         }
     }
 
@@ -196,14 +246,13 @@
 
     private static String rollbackTypeToString(int type) {
         switch (type) {
-            case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
                 return "ROLLBACK_INITIATE";
-            case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
                 return "ROLLBACK_SUCCESS";
-            case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
                 return "ROLLBACK_FAILURE";
-            case FrameworkStatsLog
-                        .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
                 return "ROLLBACK_BOOT_TRIGGERED";
             default:
                 return "UNKNOWN";
@@ -212,16 +261,16 @@
 
     private static String rollbackReasonToString(int reason) {
         switch (reason) {
-            case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH:
                 return "REASON_NATIVE_CRASH";
-            case FrameworkStatsLog
-                    .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_EXPLICIT_HEALTH_CHECK:
                 return "REASON_EXPLICIT_HEALTH_CHECK";
-            case FrameworkStatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_CRASH:
                 return "REASON_APP_CRASH";
-            case FrameworkStatsLog
-                        .WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_APP_NOT_RESPONDING:
                 return "REASON_APP_NOT_RESPONDING";
+            case WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_REASON__REASON_NATIVE_CRASH_DURING_BOOT:
+                return "REASON_NATIVE_CRASH_DURING_BOOT";
             default:
                 return "UNKNOWN";
         }
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
new file mode 100644
index 0000000..94decc7
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.ITaskOrganizer;
+import android.view.SurfaceControl;
+
+import java.util.HashMap;
+
+/**
+ * Utility class for collecting and merging transactions from various sources asynchronously.
+ * For example to use to synchronously resize all the children of a window container
+ *   1. Open a new sync set, and pass the listener that will be invoked
+ *        int id startSyncSet(TransactionReadyListener)
+ *      the returned ID will be eventually passed to the TransactionReadyListener in combination
+ *      with the prepared transaction. You also use it to refer to the operation in future steps.
+ *   2. Ask each child to participate:
+ *       addToSyncSet(int id, WindowContainer wc)
+ *      if the child thinks it will be affected by a configuration change (a.k.a. has a visible
+ *      window in its sub hierarchy, then we will increment a counter of expected callbacks
+ *      At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy
+ *      updates in to the sync engine.
+ *   3. Apply your configuration changes to the window containers.
+ *   4. Tell the engine that the sync set is ready
+ *       setReady(int id)
+ *   5. If there were no sub windows anywhere in the hierarchy to wait on, then
+ *      transactionReady is immediately invoked, otherwise all the windows are poked
+ *      to redraw and to deliver a buffer to WMS#finishDrawing.
+ *      Once all this drawing is complete the combined transaction of all the buffers
+ *      and pending transaction hierarchy changes will be delivered to the TransactionReadyListener
+ */
+class BLASTSyncEngine {
+    private static final String TAG = "BLASTSyncEngine";
+
+    interface TransactionReadyListener {
+        void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction);
+    };
+
+    // Holds state associated with a single synchronous set of operations.
+    class SyncState implements TransactionReadyListener {
+        int mSyncId;
+        SurfaceControl.Transaction mMergedTransaction;
+        int mRemainingTransactions;
+        TransactionReadyListener mListener;
+        boolean mReady = false;
+
+        private void tryFinish() {
+            if (mRemainingTransactions == 0 && mReady) {
+                mListener.transactionReady(mSyncId, mMergedTransaction);
+                mPendingSyncs.remove(mSyncId);
+            }
+        }
+
+        public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
+            mRemainingTransactions--;
+            mMergedTransaction.merge(mergedTransaction);
+            tryFinish();
+        }
+
+        void setReady() {
+            mReady = true;
+            tryFinish();
+        }
+
+        boolean addToSync(WindowContainer wc) {
+            if (wc.prepareForSync(this, mSyncId)) {
+                mRemainingTransactions++;
+                return true;
+            }
+            return false;
+        }
+
+        SyncState(TransactionReadyListener l, int id) {
+            mListener = l;
+            mSyncId = id;
+            mMergedTransaction = new SurfaceControl.Transaction();
+            mRemainingTransactions = 0;
+        }
+    };
+
+    int mNextSyncId = 0;
+
+    final HashMap<Integer, SyncState> mPendingSyncs = new HashMap();
+
+    BLASTSyncEngine() {
+    }
+
+    int startSyncSet(TransactionReadyListener listener) {
+        final int id = mNextSyncId++;
+        final SyncState s = new SyncState(listener, id);
+        mPendingSyncs.put(id, s);
+        return id;
+    }
+
+    boolean addToSyncSet(int id, WindowContainer wc) {
+        final SyncState st = mPendingSyncs.get(id);
+        return st.addToSync(wc);
+    }
+
+    // TODO(b/148476626): TIMEOUTS!
+    void setReady(int id) {
+        final SyncState st = mPendingSyncs.get(id);
+        st.setReady();
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index efe79b3..e71371a 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -526,7 +526,9 @@
             mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
             mIsWaitingForRemoteRotation = false;
             mDisplayContent.sendNewConfiguration();
-            mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+            if (t != null) {
+                mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 0418afaf..7986659 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -240,6 +240,10 @@
             target = target.getWindow().getImeControlTarget();
         }
 
+        if (mWin != null && mWin.getSurfaceControl() == null) {
+            // if window doesn't have a surface, set it null and return.
+            setWindow(null, null, null);
+        }
         if (mWin == null) {
             mControlTarget = target;
             return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eca6da3..0e500f9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -79,11 +79,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
-import static com.android.server.wm.TaskProto.FILLS_PARENT;
-import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
-import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
-import static com.android.server.wm.TaskProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
@@ -125,7 +120,6 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.DisplayMetrics;
 import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.ITaskOrganizer;
 import android.view.RemoteAnimationTarget;
@@ -3199,12 +3193,16 @@
         info.lastActiveTime = lastActiveTime;
         info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription());
         info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
-        info.resizeMode = mResizeMode;
         info.configuration.setTo(getConfiguration());
         info.token = mRemoteToken;
         // Get's the first non-undefined activity type among this and children. Can't use
         // configuration.windowConfiguration because that would only be this level.
         info.topActivityType = getActivityType();
+
+        //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child
+        //                    order changes.
+        final Task top = getTopMostTask();
+        info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 0733a72..0a0530c9 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -23,6 +23,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -38,6 +40,7 @@
 import android.util.Slog;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
+import android.view.SurfaceControl;
 import android.view.WindowContainerTransaction;
 
 import com.android.internal.util.function.pooled.PooledConsumer;
@@ -46,6 +49,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.WeakHashMap;
 
@@ -53,7 +57,8 @@
  * Stores the TaskOrganizers associated with a given windowing mode and
  * their associated state.
  */
-class TaskOrganizerController extends ITaskOrganizerController.Stub {
+class TaskOrganizerController extends ITaskOrganizerController.Stub
+    implements BLASTSyncEngine.TransactionReadyListener {
     private static final String TAG = "TaskOrganizerController";
 
     /** Flag indicating that an applied transaction may have effected lifecycle */
@@ -164,6 +169,8 @@
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
 
+    private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
+
     final ActivityTaskManagerService mService;
 
     RunningTaskInfo mTmpTaskInfo;
@@ -223,7 +230,7 @@
     }
 
     void onTaskAppeared(ITaskOrganizer organizer, Task task) {
-        TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
+        final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
         state.addTask(task);
     }
 
@@ -371,6 +378,45 @@
         }
     }
 
+    @Override
+    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent) {
+        enforceStackPermission("getChildTasks()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (parent == null) {
+                    throw new IllegalArgumentException("Can't get children of null parent");
+                }
+                final WindowContainer container = WindowContainer.fromBinder(parent.asBinder());
+                if (container == null) {
+                    Slog.e(TAG, "Can't get children of " + parent + " because it is not valid.");
+                    return null;
+                }
+                // For now, only support returning children of persistent root tasks (of which the
+                // only current implementation is TaskTile).
+                if (!(container instanceof TaskTile)) {
+                    Slog.w(TAG, "Can only get children of root tasks created via createRootTask");
+                    return null;
+                }
+                ArrayList<RunningTaskInfo> out = new ArrayList<>();
+                // Tiles aren't real parents, so we need to go through stacks on the display to
+                // ensure correct ordering.
+                final DisplayContent dc = container.getDisplayContent();
+                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack as = dc.getStackAt(i);
+                    if (as.getTile() == container) {
+                        final RunningTaskInfo info = new RunningTaskInfo();
+                        as.fillTaskInfo(info);
+                        out.add(info);
+                    }
+                }
+                return out;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private int sanitizeAndApplyChange(WindowContainer container,
             WindowContainerTransaction.Change change) {
         if (!(container instanceof Task)) {
@@ -401,6 +447,54 @@
         return effects;
     }
 
+    private int sanitizeAndApplyHierarchyOp(WindowContainer container,
+            WindowContainerTransaction.HierarchyOp hop) {
+        if (!(container instanceof Task)) {
+            throw new IllegalArgumentException("Invalid container in hierarchy op");
+        }
+        if (hop.isReparent()) {
+            // special case for tiles since they are "virtual" parents
+            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+                ActivityStack as = (ActivityStack) container;
+                TaskTile newParent = hop.getNewParent() == null ? null
+                        : (TaskTile) WindowContainer.fromBinder(hop.getNewParent());
+                if (as.getTile() != newParent) {
+                    if (as.getTile() != null) {
+                        as.getTile().removeChild(as);
+                    }
+                    if (newParent != null) {
+                        if (!as.affectedBySplitScreenResize()) {
+                            return 0;
+                        }
+                        newParent.addChild(as, POSITION_TOP);
+                    }
+                }
+                if (hop.getToTop()) {
+                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                } else {
+                    as.getDisplay().positionStackAtBottom(as);
+                }
+            } else if (container instanceof Task) {
+                throw new RuntimeException("Reparenting leaf Tasks is not supported now.");
+            }
+        } else {
+            // Ugh, of course ActivityStack has its own special reorder logic...
+            if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
+                ActivityStack as = (ActivityStack) container;
+                if (hop.getToTop()) {
+                    as.getDisplay().positionStackAtTop(as, false /* includingParents */);
+                } else {
+                    as.getDisplay().positionStackAtBottom(as);
+                }
+            } else {
+                container.getParent().positionChildAt(
+                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                        container, false /* includingParents */);
+            }
+        }
+        return TRANSACT_EFFECTS_LIFECYCLE;
+    }
+
     private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
             int windowMask, Configuration config) {
         if ((container instanceof ActivityStack)
@@ -429,15 +523,35 @@
     }
 
     @Override
-    public void applyContainerTransaction(WindowContainerTransaction t) {
+    public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) {
         enforceStackPermission("applyContainerTransaction()");
+        int syncId = -1;
         if (t == null) {
-            return;
+            throw new IllegalArgumentException(
+                    "Null transaction passed to applyContainerTransaction");
         }
         long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
                 int effects = 0;
+
+                /**
+                 * If organizer is non-null we are looking to synchronize this transaction
+                 * by collecting all the results in to a SurfaceFlinger transaction and
+                 * then delivering that to the given organizers transaction ready callback.
+                 * See {@link BLASTSyncEngine} for the details of the operation. But at
+                 * a high level we create a sync operation with a given ID and an associated
+                 * organizer. Then we notify each WindowContainer in this WindowContainer
+                 * transaction that it is participating in a sync operation with that
+                 * ID. Once everything is notified we tell the BLASTSyncEngine
+                 * "setSyncReady" which means that we have added everything
+                 * to the set. At any point after this, all the WindowContainers
+                 * will eventually finish applying their changes and notify the
+                 * BLASTSyncEngine which will deliver the Transaction to the organizer.
+                 */
+                if (organizer != null) {
+                    syncId = startSyncWithOrganizer(organizer);
+                }
                 mService.deferWindowLayout();
                 try {
                     ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
@@ -446,15 +560,25 @@
                     while (entries.hasNext()) {
                         final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
                                 entries.next();
-                        final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
-                                entry.getKey()).getContainer();
+                        final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
                         int containerEffect = applyWindowContainerChange(wc, entry.getValue());
                         effects |= containerEffect;
+
                         // Lifecycle changes will trigger ensureConfig for everything.
                         if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
                                 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
                             haveConfigChanges.add(wc);
                         }
+                        if (syncId >= 0) {
+                            mBLASTSyncEngine.addToSyncSet(syncId, wc);
+                        }
+                    }
+                    // Hierarchy changes
+                    final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
+                    for (int i = 0, n = hops.size(); i < n; ++i) {
+                        final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+                        final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
+                        effects |= sanitizeAndApplyHierarchyOp(wc, hop);
                     }
                     if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
                         // Already calls ensureActivityConfig
@@ -475,10 +599,38 @@
                     }
                 } finally {
                     mService.continueWindowLayout();
+                    if (syncId >= 0) {
+                        setSyncReady(syncId);
+                    }
                 }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+        return syncId;
+    }
+
+    @Override
+    public void transactionReady(int id, SurfaceControl.Transaction sc) {
+        final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id);
+        if (organizer == null) {
+            Slog.e(TAG, "Got transaction complete for unexpected ID");
+        }
+        try {
+            organizer.transactionReady(id, sc);
+        } catch (RemoteException e) {
+        }
+
+        mTaskOrganizersByPendingSyncId.remove(id);
+    }
+
+    int startSyncWithOrganizer(ITaskOrganizer organizer) {
+        int id = mBLASTSyncEngine.startSyncSet(this);
+        mTaskOrganizersByPendingSyncId.put(id, organizer);
+        return id;
+    }
+
+    void setSyncReady(int id) {
+        mBLASTSyncEngine.setReady(id);
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 8bbb0d7..b5892b9 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -20,6 +20,11 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
@@ -29,7 +34,6 @@
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.IActivityTaskManager;
 import android.graphics.Point;
@@ -55,11 +59,10 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.internal.policy.TaskResizingAlgorithm.CtrlType;
 import com.android.server.protolog.common.ProtoLog;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 class TaskPositioner implements IBinder.DeathRecipient {
     private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
     private static final String TAG_LOCAL = "TaskPositioner";
@@ -67,33 +70,10 @@
 
     private static Factory sFactory;
 
-    @IntDef(flag = true,
-            value = {
-                    CTRL_NONE,
-                    CTRL_LEFT,
-                    CTRL_RIGHT,
-                    CTRL_TOP,
-                    CTRL_BOTTOM
-            })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface CtrlType {}
-
-    private static final int CTRL_NONE   = 0x0;
-    private static final int CTRL_LEFT   = 0x1;
-    private static final int CTRL_RIGHT  = 0x2;
-    private static final int CTRL_TOP    = 0x4;
-    private static final int CTRL_BOTTOM = 0x8;
-
     public static final float RESIZING_HINT_ALPHA = 0.5f;
 
     public static final int RESIZING_HINT_DURATION_MS = 0;
 
-    // The minimal aspect ratio which needs to be met to count as landscape (or 1/.. for portrait).
-    // Note: We do not use the 1.33 from the CDD here since the user is allowed to use what ever
-    // aspect he desires.
-    @VisibleForTesting
-    static final float MIN_ASPECT = 1.2f;
-
     private final WindowManagerService mService;
     private final IActivityTaskManager mActivityManager;
     private WindowPositionerEventReceiver mInputEventReceiver;
@@ -477,122 +457,13 @@
      */
     @VisibleForTesting
     void resizeDrag(float x, float y) {
-        // This is a resizing operation.
-        // We need to keep various constraints:
-        // 1. mMinVisible[Width/Height] <= [width/height] <= mMaxVisibleSize.[x/y]
-        // 2. The orientation is kept - if required.
-        final int deltaX = Math.round(x - mStartDragX);
-        final int deltaY = Math.round(y - mStartDragY);
-        int left = mWindowOriginalBounds.left;
-        int top = mWindowOriginalBounds.top;
-        int right = mWindowOriginalBounds.right;
-        int bottom = mWindowOriginalBounds.bottom;
-
-        // Calculate the resulting width and height of the drag operation.
-        int width = right - left;
-        int height = bottom - top;
-        if ((mCtrlType & CTRL_LEFT) != 0) {
-            width = Math.max(mMinVisibleWidth, width - deltaX);
-        } else if ((mCtrlType & CTRL_RIGHT) != 0) {
-            width = Math.max(mMinVisibleWidth, width + deltaX);
-        }
-        if ((mCtrlType & CTRL_TOP) != 0) {
-            height = Math.max(mMinVisibleHeight, height - deltaY);
-        } else if ((mCtrlType & CTRL_BOTTOM) != 0) {
-            height = Math.max(mMinVisibleHeight, height + deltaY);
-        }
-
-        // If we have to preserve the orientation - check that we are doing so.
-        final float aspect = (float) width / (float) height;
-        if (mPreserveOrientation && ((mStartOrientationWasLandscape && aspect < MIN_ASPECT)
-                || (!mStartOrientationWasLandscape && aspect > (1.0 / MIN_ASPECT)))) {
-            // Calculate 2 rectangles fulfilling all requirements for either X or Y being the major
-            // drag axis. What ever is producing the bigger rectangle will be chosen.
-            int width1;
-            int width2;
-            int height1;
-            int height2;
-            if (mStartOrientationWasLandscape) {
-                // Assuming that the width is our target we calculate the height.
-                width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
-                height1 = Math.min(height, Math.round((float)width1 / MIN_ASPECT));
-                if (height1 < mMinVisibleHeight) {
-                    // If the resulting height is too small we adjust to the minimal size.
-                    height1 = mMinVisibleHeight;
-                    width1 = Math.max(mMinVisibleWidth,
-                            Math.min(mMaxVisibleSize.x, Math.round((float)height1 * MIN_ASPECT)));
-                }
-                // Assuming that the height is our target we calculate the width.
-                height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
-                width2 = Math.max(width, Math.round((float)height2 * MIN_ASPECT));
-                if (width2 < mMinVisibleWidth) {
-                    // If the resulting width is too small we adjust to the minimal size.
-                    width2 = mMinVisibleWidth;
-                    height2 = Math.max(mMinVisibleHeight,
-                            Math.min(mMaxVisibleSize.y, Math.round((float)width2 / MIN_ASPECT)));
-                }
-            } else {
-                // Assuming that the width is our target we calculate the height.
-                width1 = Math.max(mMinVisibleWidth, Math.min(mMaxVisibleSize.x, width));
-                height1 = Math.max(height, Math.round((float)width1 * MIN_ASPECT));
-                if (height1 < mMinVisibleHeight) {
-                    // If the resulting height is too small we adjust to the minimal size.
-                    height1 = mMinVisibleHeight;
-                    width1 = Math.max(mMinVisibleWidth,
-                            Math.min(mMaxVisibleSize.x, Math.round((float)height1 / MIN_ASPECT)));
-                }
-                // Assuming that the height is our target we calculate the width.
-                height2 = Math.max(mMinVisibleHeight, Math.min(mMaxVisibleSize.y, height));
-                width2 = Math.min(width, Math.round((float)height2 / MIN_ASPECT));
-                if (width2 < mMinVisibleWidth) {
-                    // If the resulting width is too small we adjust to the minimal size.
-                    width2 = mMinVisibleWidth;
-                    height2 = Math.max(mMinVisibleHeight,
-                            Math.min(mMaxVisibleSize.y, Math.round((float)width2 * MIN_ASPECT)));
-                }
-            }
-
-            // Use the bigger of the two rectangles if the major change was positive, otherwise
-            // do the opposite.
-            final boolean grows = width > (right - left) || height > (bottom - top);
-            if (grows == (width1 * height1 > width2 * height2)) {
-                width = width1;
-                height = height1;
-            } else {
-                width = width2;
-                height = height2;
-            }
-        }
-
-        // Update mWindowDragBounds to the new drag size.
-        updateDraggedBounds(left, top, right, bottom, width, height);
+        updateDraggedBounds(TaskResizingAlgorithm.resizeDrag(x, y, mStartDragX, mStartDragY,
+                mWindowOriginalBounds, mCtrlType, mMinVisibleWidth, mMinVisibleHeight,
+                mMaxVisibleSize, mPreserveOrientation, mStartOrientationWasLandscape));
     }
 
-    /**
-     * Given the old coordinates and the new width and height, update the mWindowDragBounds.
-     *
-     * @param left      The original left bound before the user started dragging.
-     * @param top       The original top bound before the user started dragging.
-     * @param right     The original right bound before the user started dragging.
-     * @param bottom    The original bottom bound before the user started dragging.
-     * @param newWidth  The new dragged width.
-     * @param newHeight The new dragged height.
-     */
-    void updateDraggedBounds(int left, int top, int right, int bottom, int newWidth,
-                             int newHeight) {
-        // Generate the final bounds by keeping the opposite drag edge constant.
-        if ((mCtrlType & CTRL_LEFT) != 0) {
-            left = right - newWidth;
-        } else { // Note: The right might have changed - if we pulled at the right or not.
-            right = left + newWidth;
-        }
-        if ((mCtrlType & CTRL_TOP) != 0) {
-            top = bottom - newHeight;
-        } else { // Note: The height might have changed - if we pulled at the bottom or not.
-            bottom = top + newHeight;
-        }
-
-        mWindowDragBounds.set(left, top, right, bottom);
+    private void updateDraggedBounds(Rect newBounds) {
+        mWindowDragBounds.set(newBounds);
 
         checkBoundsForOrientationViolations(mWindowDragBounds);
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8672315..504aa2d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -94,7 +94,8 @@
  * changes are made to this class.
  */
 class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
-        implements Comparable<WindowContainer>, Animatable {
+        implements Comparable<WindowContainer>, Animatable,
+                   BLASTSyncEngine.TransactionReadyListener {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM;
 
@@ -260,6 +261,12 @@
      */
     RemoteToken mRemoteToken = null;
 
+    BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine();
+    SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction();
+    boolean mUsingBLASTSyncTransaction = false;
+    BLASTSyncEngine.TransactionReadyListener mWaitingListener;
+    int mWaitingSyncId;
+
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
         mPendingTransaction = wms.mTransactionFactory.get();
@@ -1837,6 +1844,10 @@
 
     @Override
     public Transaction getPendingTransaction() {
+        if (mUsingBLASTSyncTransaction) {
+            return mBLASTSyncTransaction;
+        }
+
         final DisplayContent displayContent = getDisplayContent();
         if (displayContent != null && displayContent != this) {
             return displayContent.getPendingTransaction();
@@ -2285,6 +2296,10 @@
         return mRemoteToken;
     }
 
+    static WindowContainer fromBinder(IBinder binder) {
+        return RemoteToken.fromBinder(binder).getContainer();
+    }
+
     static class RemoteToken extends IWindowContainer.Stub {
         final WeakReference<WindowContainer> mWeakRef;
 
@@ -2316,4 +2331,38 @@
             return sb.toString();
         }
     }
+
+    @Override
+    public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) {
+        mergedTransaction.merge(mBLASTSyncTransaction);
+        mUsingBLASTSyncTransaction = false;
+
+        mWaitingListener.transactionReady(mWaitingSyncId, mergedTransaction);
+
+        mWaitingListener = null;
+        mWaitingSyncId = -1;
+    }
+
+    boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
+            int waitingId) {
+        boolean willSync = false;
+        if (!isVisible()) {
+            return willSync;
+        }
+        mUsingBLASTSyncTransaction = true;
+
+        int localId = mBLASTSyncEngine.startSyncSet(this);
+        for (int i = 0; i < mChildren.size(); i++) {
+            final WindowContainer child = mChildren.get(i);
+            willSync = mBLASTSyncEngine.addToSyncSet(localId, child) | willSync;
+        }
+
+        // Make sure to set these before we call setReady in case the sync was a no-op
+        mWaitingSyncId = waitingId;
+        mWaitingListener = waitingListener;
+
+        mBLASTSyncEngine.setReady(localId);
+
+        return willSync;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6e1f46bb..6330985 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2505,7 +2505,7 @@
                 WindowState win = windowForClientLocked(session, client, false);
                 ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
                         win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
-                if (win != null && win.mWinAnimator.finishDrawingLocked(postDrawTransaction)) {
+                if (win != null && win.finishDrawing(postDrawTransaction)) {
                     if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
                         win.getDisplayContent().pendingLayoutChanges |=
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
@@ -5687,6 +5687,12 @@
 
     @Override
     public void setForceShowSystemBars(boolean show) {
+        boolean isAutomotive = mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+        if (!isAutomotive) {
+            throw new UnsupportedOperationException("Force showing system bars is only supported"
+                    + "for Automotive use cases.");
+        }
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Caller does not hold permission "
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 87b04b2..c7d00ec 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -178,7 +178,7 @@
     private long mLastActivityFinishTime;
 
     // Last configuration that was reported to the process.
-    private final Configuration mLastReportedConfiguration;
+    private final Configuration mLastReportedConfiguration = new Configuration();
     // Configuration that is waiting to be dispatched to the process.
     private Configuration mPendingConfiguration;
     private final Configuration mNewOverrideConfig = new Configuration();
@@ -192,7 +192,7 @@
     /** Whether our process is currently running a {@link IRemoteAnimationRunner} */
     private boolean mRunningRemoteAnimation;
 
-    public WindowProcessController(ActivityTaskManagerService atm, ApplicationInfo info,
+    public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
             String name, int uid, int userId, Object owner, WindowProcessListener listener) {
         mInfo = info;
         mName = name;
@@ -201,11 +201,8 @@
         mOwner = owner;
         mListener = listener;
         mAtm = atm;
-        mLastReportedConfiguration = new Configuration();
         mDisplayId = INVALID_DISPLAY;
-        if (atm != null) {
-            onConfigurationChanged(atm.getGlobalConfiguration());
-        }
+        onConfigurationChanged(atm.getGlobalConfiguration());
     }
 
     public void setPid(int pid) {
@@ -220,6 +217,11 @@
     public void setThread(IApplicationThread thread) {
         synchronized (mAtm.mGlobalLockWithoutBoost) {
             mThread = thread;
+            // In general this is called from attaching application, so the last configuration
+            // has been sent to client by {@link android.app.IApplicationThread#bindApplication}.
+            // If this process is system server, it is fine because system is booting and a new
+            // configuration will update when display is ready.
+            setLastReportedConfiguration(getConfiguration());
         }
     }
 
@@ -1060,7 +1062,6 @@
         mNewOverrideConfig.setTo(mergedOverrideConfig);
         mNewOverrideConfig.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
         super.onRequestedOverrideConfigurationChanged(mNewOverrideConfig);
-        updateConfiguration();
     }
 
     private void updateConfiguration() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 42e5bbc..b336f8d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5689,4 +5689,30 @@
     SurfaceControl getDeferTransactionBarrier() {
         return mWinAnimator.getDeferTransactionBarrier();
     }
+
+    @Override
+    boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener,
+            int waitingId) {
+        // TODO(b/148871522): Support child window
+        mWaitingListener = waitingListener;
+        mWaitingSyncId = waitingId;
+        mUsingBLASTSyncTransaction = true;
+        return true;
+    }
+
+    boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
+        if (!mUsingBLASTSyncTransaction) {
+            return mWinAnimator.finishDrawingLocked(postDrawTransaction);
+        }
+        if (postDrawTransaction == null) {
+            postDrawTransaction = new SurfaceControl.Transaction();
+        }
+        postDrawTransaction.merge(mBLASTSyncTransaction);
+        mWaitingListener.transactionReady(mWaitingSyncId, postDrawTransaction);
+        mUsingBLASTSyncTransaction = false;
+
+        mWaitingSyncId = 0;
+        mWaitingListener = null;
+        return mWinAnimator.finishDrawingLocked(null);
+    }
 }
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 390068e..49c7e0a 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -13,7 +13,6 @@
     ],
 
     srcs: [
-        ":graphicsstats_proto",
         ":lib_alarmManagerService_native",
         "BroadcastRadio/JavaRef.cpp",
         "BroadcastRadio/NativeCallbackThread.cpp",
@@ -53,7 +52,6 @@
         "com_android_server_UsbHostManager.cpp",
         "com_android_server_VibratorService.cpp",
         "com_android_server_PersistentDataBlockService.cpp",
-        "com_android_server_GraphicsStatsService.cpp",
         "com_android_server_am_CachedAppOptimizer.cpp",
         "com_android_server_am_LowMemDetector.cpp",
         "com_android_server_incremental_IncrementalManagerService.cpp",
@@ -107,8 +105,7 @@
         "libinputflinger",
         "libinputflinger_base",
         "libinputservice",
-        "libprotobuf-cpp-lite",
-        "libprotoutil",
+        "libstatshidl",
         "libstatspull",
         "libstatssocket",
         "libstatslog",
@@ -155,6 +152,7 @@
         "android.hardware.vr@1.0",
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
+        "android.frameworks.stats@1.0",
         "android.system.suspend@1.0",
         "service.incremental",
         "suspend_control_aidl_interface-cpp",
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
deleted file mode 100644
index aa7067e..0000000
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "GraphicsStatsService"
-
-#include <jni.h>
-#include <log/log.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <JankTracker.h>
-#include <service/GraphicsStatsService.h>
-#include <stats_pull_atom_callback.h>
-#include <stats_event.h>
-#include <statslog.h>
-#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
-#include <android/util/ProtoOutputStream.h>
-#include "android/graphics/Utils.h"
-#include "core_jni_helpers.h"
-#include "protos/graphicsstats.pb.h"
-#include <cstring>
-#include <memory>
-
-namespace android {
-
-using namespace android::uirenderer;
-
-static jint getAshmemSize(JNIEnv*, jobject) {
-    return sizeof(ProfileData);
-}
-
-static jlong createDump(JNIEnv*, jobject, jint fd, jboolean isProto) {
-    GraphicsStatsService::Dump* dump = GraphicsStatsService::createDump(fd, isProto
-            ? GraphicsStatsService::DumpType::Protobuf : GraphicsStatsService::DumpType::Text);
-    return reinterpret_cast<jlong>(dump);
-}
-
-static void addToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath, jstring jpackage,
-        jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
-    std::string path;
-    const ProfileData* data = nullptr;
-    LOG_ALWAYS_FATAL_IF(jdata == nullptr && jpath == nullptr, "Path and data can't both be null");
-    ScopedByteArrayRO buffer{env};
-    if (jdata != nullptr) {
-        buffer.reset(jdata);
-        LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
-                "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
-        data = reinterpret_cast<const ProfileData*>(buffer.get());
-    }
-    if (jpath != nullptr) {
-        ScopedUtfChars pathChars(env, jpath);
-        LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
-        path.assign(pathChars.c_str(), pathChars.size());
-    }
-    ScopedUtfChars packageChars(env, jpackage);
-    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    LOG_ALWAYS_FATAL_IF(!dump, "null passed for dump pointer");
-
-    const std::string package(packageChars.c_str(), packageChars.size());
-    GraphicsStatsService::addToDump(dump, path, package, versionCode, startTime, endTime, data);
-}
-
-static void addFileToDump(JNIEnv* env, jobject, jlong dumpPtr, jstring jpath) {
-    ScopedUtfChars pathChars(env, jpath);
-    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
-    const std::string path(pathChars.c_str(), pathChars.size());
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    GraphicsStatsService::addToDump(dump, path);
-}
-
-static void finishDump(JNIEnv*, jobject, jlong dumpPtr) {
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    GraphicsStatsService::finishDump(dump);
-}
-
-static jlong finishDumpInMemory(JNIEnv* env, jobject, jlong dumpPtr) {
-    GraphicsStatsService::Dump* dump = reinterpret_cast<GraphicsStatsService::Dump*>(dumpPtr);
-    std::vector<uint8_t>* result = new std::vector<uint8_t>();
-    GraphicsStatsService::finishDumpInMemory(dump,
-        [](void* buffer, int bufferOffset, int bufferSize, int totalSize, void* param1, void* param2) {
-            std::vector<uint8_t>* outBuffer = reinterpret_cast<std::vector<uint8_t>*>(param2);
-            if (outBuffer->size() < totalSize) {
-                outBuffer->resize(totalSize);
-            }
-            std::memcpy(outBuffer->data() + bufferOffset, buffer, bufferSize);
-        }, env, result);
-    return reinterpret_cast<jlong>(result);
-}
-
-static void saveBuffer(JNIEnv* env, jobject clazz, jstring jpath, jstring jpackage,
-        jlong versionCode, jlong startTime, jlong endTime, jbyteArray jdata) {
-    ScopedByteArrayRO buffer(env, jdata);
-    LOG_ALWAYS_FATAL_IF(buffer.size() != sizeof(ProfileData),
-            "Buffer size %zu doesn't match expected %zu!", buffer.size(), sizeof(ProfileData));
-    ScopedUtfChars pathChars(env, jpath);
-    LOG_ALWAYS_FATAL_IF(pathChars.size() <= 0 || !pathChars.c_str(), "Failed to get path chars");
-    ScopedUtfChars packageChars(env, jpackage);
-    LOG_ALWAYS_FATAL_IF(packageChars.size() <= 0 || !packageChars.c_str(), "Failed to get path chars");
-
-    const std::string path(pathChars.c_str(), pathChars.size());
-    const std::string package(packageChars.c_str(), packageChars.size());
-    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer.get());
-    GraphicsStatsService::saveBuffer(path, package, versionCode, startTime, endTime, data);
-}
-
-static jobject gGraphicsStatsServiceObject = nullptr;
-static jmethodID gGraphicsStatsService_pullGraphicsStatsMethodID;
-
-static JNIEnv* getJNIEnv() {
-    JavaVM* vm = AndroidRuntime::getJavaVM();
-    JNIEnv* env = nullptr;
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
-            LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
-        }
-    }
-    return env;
-}
-
-using namespace google::protobuf;
-
-// Field ids taken from FrameTimingHistogram message in atoms.proto
-#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
-#define FRAME_COUNTS_FIELD_NUMBER 2
-
-static void writeCpuHistogram(AStatsEvent* event,
-                              const uirenderer::protos::GraphicsStatsProto& stat) {
-    util::ProtoOutputStream proto;
-    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
-        auto& bucket = stat.histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
-                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
-                    (int)bucket.render_millis());
-    }
-    for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
-        auto& bucket = stat.histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
-                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
-                    (long long)bucket.frame_count());
-    }
-    std::vector<uint8_t> outVector;
-    proto.serializeToVector(&outVector);
-    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
-}
-
-static void writeGpuHistogram(AStatsEvent* event,
-                              const uirenderer::protos::GraphicsStatsProto& stat) {
-    util::ProtoOutputStream proto;
-    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
-        auto& bucket = stat.gpu_histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
-                            TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
-                    (int)bucket.render_millis());
-    }
-    for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
-        auto& bucket = stat.gpu_histogram(bucketIndex);
-        proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
-                            FRAME_COUNTS_FIELD_NUMBER /* field id */,
-                    (long long)bucket.frame_count());
-    }
-    std::vector<uint8_t> outVector;
-    proto.serializeToVector(&outVector);
-    AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
-}
-
-// graphicsStatsPullCallback is invoked by statsd service to pull GRAPHICS_STATS atom.
-static AStatsManager_PullAtomCallbackReturn graphicsStatsPullCallback(int32_t atom_tag,
-                                                                      AStatsEventList* data,
-                                                                      void* cookie) {
-    JNIEnv* env = getJNIEnv();
-    if (!env) {
-        return false;
-    }
-    if (gGraphicsStatsServiceObject == nullptr) {
-        ALOGE("Failed to get graphicsstats service");
-        return AStatsManager_PULL_SKIP;
-    }
-
-    for (bool lastFullDay : {true, false}) {
-        jlong jdata = (jlong) env->CallLongMethod(
-                    gGraphicsStatsServiceObject,
-                    gGraphicsStatsService_pullGraphicsStatsMethodID,
-                    (jboolean)(lastFullDay ? JNI_TRUE : JNI_FALSE));
-        if (env->ExceptionCheck()) {
-            env->ExceptionDescribe();
-            env->ExceptionClear();
-            ALOGE("Failed to invoke graphicsstats service");
-            return AStatsManager_PULL_SKIP;
-        }
-        if (!jdata) {
-            // null means data is not available for that day.
-            continue;
-        }
-        android::uirenderer::protos::GraphicsStatsServiceDumpProto serviceDump;
-        std::vector<uint8_t>* buffer = reinterpret_cast<std::vector<uint8_t>*>(jdata);
-        std::unique_ptr<std::vector<uint8_t>> bufferRelease(buffer);
-        int dataSize = buffer->size();
-        if (!dataSize) {
-            // Data is not available for that day.
-            continue;
-        }
-        io::ArrayInputStream input{buffer->data(), dataSize};
-        bool success = serviceDump.ParseFromZeroCopyStream(&input);
-        if (!success) {
-            ALOGW("Parse failed on GraphicsStatsPuller error='%s' dataSize='%d'",
-                  serviceDump.InitializationErrorString().c_str(), dataSize);
-            return AStatsManager_PULL_SKIP;
-        }
-
-        for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
-            auto& stat = serviceDump.stats(stat_index);
-            AStatsEvent* event = AStatsEventList_addStatsEvent(data);
-            AStatsEvent_setAtomId(event, android::util::GRAPHICS_STATS);
-            AStatsEvent_writeString(event, stat.package_name().c_str());
-            AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
-            AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
-            AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
-            AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
-            AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
-            writeCpuHistogram(event, stat);
-            writeGpuHistogram(event, stat);
-            // TODO: fill in UI mainline module version, when the feature is available.
-            AStatsEvent_writeInt64(event, (int64_t)0);
-            AStatsEvent_writeBool(event, !lastFullDay);
-            AStatsEvent_build(event);
-        }
-    }
-    return AStatsManager_PULL_SUCCESS;
-}
-
-// Register a puller for GRAPHICS_STATS atom with the statsd service.
-static void nativeInit(JNIEnv* env, jobject javaObject) {
-    gGraphicsStatsServiceObject = env->NewGlobalRef(javaObject);
-    AStatsManager_PullAtomMetadata* metadata = AStatsManager_PullAtomMetadata_obtain();
-    AStatsManager_PullAtomMetadata_setCoolDownNs(metadata, 10 * 1000000);  // 10 milliseconds
-    AStatsManager_PullAtomMetadata_setTimeoutNs(metadata, 2 * NS_PER_SEC); // 2 seconds
-
-    AStatsManager_registerPullAtomCallback(android::util::GRAPHICS_STATS,
-                                           &graphicsStatsPullCallback, metadata, nullptr);
-
-    AStatsManager_PullAtomMetadata_release(metadata);
-}
-
-static void nativeDestructor(JNIEnv* env, jobject javaObject) {
-    AStatsManager_unregisterPullAtomCallback(android::util::GRAPHICS_STATS);
-    env->DeleteGlobalRef(gGraphicsStatsServiceObject);
-    gGraphicsStatsServiceObject = nullptr;
-}
-
-static const JNINativeMethod sMethods[] = {
-    { "nGetAshmemSize", "()I", (void*) getAshmemSize },
-    { "nCreateDump", "(IZ)J", (void*) createDump },
-    { "nAddToDump", "(JLjava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) addToDump },
-    { "nAddToDump", "(JLjava/lang/String;)V", (void*) addFileToDump },
-    { "nFinishDump", "(J)V", (void*) finishDump },
-    { "nFinishDumpInMemory", "(J)J", (void*) finishDumpInMemory },
-    { "nSaveBuffer", "(Ljava/lang/String;Ljava/lang/String;JJJ[B)V", (void*) saveBuffer },
-    { "nativeInit", "()V", (void*) nativeInit },
-    { "nativeDestructor",   "()V",     (void*)nativeDestructor }
-};
-
-int register_android_server_GraphicsStatsService(JNIEnv* env)
-{
-    jclass graphicsStatsService_class = FindClassOrDie(env,
-            "com/android/server/GraphicsStatsService");
-    gGraphicsStatsService_pullGraphicsStatsMethodID = GetMethodIDOrDie(env,
-            graphicsStatsService_class, "pullGraphicsStats", "(Z)J");
-    return jniRegisterNativeMethods(env, "com/android/server/GraphicsStatsService",
-                                    sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 67254b8..279ea4b 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -29,6 +29,7 @@
 #include <schedulerservice/SchedulingPolicyService.h>
 #include <sensorservice/SensorService.h>
 #include <sensorservicehidl/SensorManager.h>
+#include <stats/StatsHal.h>
 
 #include <bionic/malloc.h>
 #include <bionic/reserved_signals.h>
@@ -59,6 +60,8 @@
     using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService;
     using ::android::frameworks::sensorservice::V1_0::ISensorManager;
     using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager;
+    using ::android::frameworks::stats::V1_0::IStats;
+    using ::android::frameworks::stats::V1_0::implementation::StatsHal;
     using ::android::hardware::configureRpcThreadpool;
 
     status_t err;
@@ -75,6 +78,10 @@
     sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService();
     err = schedulingService->registerAsService();
     ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err);
+
+    sp<IStats> statsHal = new StatsHal();
+    err = statsHal->registerAsService();
+    ALOGE_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err);
 }
 
 static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 1202ad3..c186494 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -49,7 +49,7 @@
 int register_android_server_Watchdog(JNIEnv* env);
 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
 int register_android_server_SyntheticPasswordManager(JNIEnv* env);
-int register_android_server_GraphicsStatsService(JNIEnv* env);
+int register_android_graphics_GraphicsStatsService(JNIEnv* env);
 int register_android_hardware_display_DisplayViewport(JNIEnv* env);
 int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
 int register_android_server_net_NetworkStatsService(JNIEnv* env);
@@ -102,7 +102,7 @@
     register_android_server_HardwarePropertiesManagerService(env);
     register_android_server_storage_AppFuse(env);
     register_android_server_SyntheticPasswordManager(env);
-    register_android_server_GraphicsStatsService(env);
+    register_android_graphics_GraphicsStatsService(env);
     register_android_hardware_display_DisplayViewport(env);
     register_android_server_net_NetworkStatsFactory(env);
     register_android_server_net_NetworkStatsService(env);
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 0941831..b2c316a 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -16,6 +16,7 @@
 
 #include "BinderIncrementalService.h"
 
+#include <android-base/logging.h>
 #include <binder/IResultReceiver.h>
 #include <binder/PermissionCache.h>
 #include <incfs.h>
@@ -24,7 +25,6 @@
 #include "jni.h"
 #include "nativehelper/JNIHelp.h"
 #include "path.h"
-#include <android-base/logging.h>
 
 using namespace std::literals;
 using namespace android::incremental;
@@ -277,6 +277,13 @@
     return ok();
 }
 
+binder::Status BinderIncrementalService::configureNativeBinaries(
+        int32_t storageId, const std::string& apkFullPath, const std::string& libDirRelativePath,
+        const std::string& abi, bool* _aidl_return) {
+    *_aidl_return = mImpl.configureNativeBinaries(storageId, apkFullPath, libDirRelativePath, abi);
+    return ok();
+}
+
 } // namespace android::os::incremental
 
 jlong Incremental_IncrementalService_Start() {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 8a09977..51d7de3 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -28,11 +28,11 @@
 class BinderIncrementalService : public BnIncrementalService,
                                  public BinderService<BinderIncrementalService> {
 public:
-    BinderIncrementalService(const sp<IServiceManager> &sm);
+    BinderIncrementalService(const sp<IServiceManager>& sm);
 
-    static BinderIncrementalService *start();
-    static const char16_t *getServiceName() { return u"incremental_service"; }
-    status_t dump(int fd, const Vector<String16> &args) final;
+    static BinderIncrementalService* start();
+    static const char16_t* getServiceName() { return u"incremental_service"; }
+    status_t dump(int fd, const Vector<String16>& args) final;
 
     void onSystemReady();
     void onInvalidStorage(int mountId);
@@ -70,6 +70,9 @@
                                    std::vector<uint8_t>* _aidl_return) final;
     binder::Status startLoading(int32_t storageId, bool* _aidl_return) final;
     binder::Status deleteStorage(int32_t storageId) final;
+    binder::Status configureNativeBinaries(int32_t storageId, const std::string& apkFullPath,
+                                           const std::string& libDirRelativePath,
+                                           const std::string& abi, bool* _aidl_return) final;
 
 private:
     android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index dbd97cf..3b51377 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -60,6 +60,9 @@
     static constexpr auto storagePrefix = "st"sv;
     static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
     static constexpr auto infoMdName = ".info"sv;
+    static constexpr auto libDir = "lib"sv;
+    static constexpr auto libSuffix = ".so"sv;
+    static constexpr auto blockSize = 4096;
 };
 
 static const Constants& constants() {
@@ -259,16 +262,18 @@
 
 inline const char* toString(TimePoint t) {
     using SystemClock = std::chrono::system_clock;
-    time_t time = SystemClock::to_time_t(SystemClock::now() + std::chrono::duration_cast<SystemClock::duration>(t - Clock::now()));
+    time_t time = SystemClock::to_time_t(
+            SystemClock::now() +
+            std::chrono::duration_cast<SystemClock::duration>(t - Clock::now()));
     return std::ctime(&time);
 }
 
 inline const char* toString(IncrementalService::BindKind kind) {
     switch (kind) {
-    case IncrementalService::BindKind::Temporary:
-        return "Temporary";
-    case IncrementalService::BindKind::Permanent:
-        return "Permanent";
+        case IncrementalService::BindKind::Temporary:
+            return "Temporary";
+        case IncrementalService::BindKind::Permanent:
+            return "Permanent";
     }
 }
 
@@ -1124,6 +1129,122 @@
     return true;
 }
 
+// Extract lib filse from zip, create new files in incfs and write data to them
+bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
+                                                 std::string_view libDirRelativePath,
+                                                 std::string_view abi) {
+    const auto ifs = getIfs(storage);
+    // First prepare target directories if they don't exist yet
+    if (auto res = makeDirs(storage, libDirRelativePath, 0755)) {
+        LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
+                   << " errno: " << res;
+        return false;
+    }
+
+    std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data()));
+    if (!zipFile) {
+        LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
+        return false;
+    }
+    void* cookie = nullptr;
+    const auto libFilePrefix = path::join(constants().libDir, abi);
+    if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
+                                       constants().libSuffix.data() /* suffix */)) {
+        LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
+        return false;
+    }
+    ZipEntryRO entry = nullptr;
+    bool success = true;
+    while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) {
+        char fileName[PATH_MAX];
+        if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) {
+            continue;
+        }
+        const auto libName = path::basename(fileName);
+        const auto targetLibPath = path::join(libDirRelativePath, libName);
+        const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
+        // If the extract file already exists, skip
+        struct stat st;
+        if (stat(targetLibPathAbsolute.c_str(), &st) == 0) {
+            LOG(INFO) << "Native lib file already exists: " << targetLibPath
+                      << "; skipping extraction";
+            continue;
+        }
+
+        uint32_t uncompressedLen;
+        if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr,
+                                         nullptr, nullptr)) {
+            LOG(ERROR) << "Failed to read native lib entry: " << fileName;
+            success = false;
+            break;
+        }
+
+        // Create new lib file without signature info
+        incfs::NewFileParams libFileParams;
+        libFileParams.size = uncompressedLen;
+        libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE;
+        // Metadata of the new lib file is its relative path
+        IncFsSpan libFileMetadata;
+        libFileMetadata.data = targetLibPath.c_str();
+        libFileMetadata.size = targetLibPath.size();
+        libFileParams.metadata = libFileMetadata;
+        incfs::FileId libFileId = idFromMetadata(targetLibPath);
+        if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) {
+            LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
+            success = false;
+            // If one lib file fails to be created, abort others as well
+            break;
+        }
+
+        // Write extracted data to new file
+        std::vector<uint8_t> libData(uncompressedLen);
+        if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) {
+            LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName;
+            success = false;
+            break;
+        }
+        android::base::unique_fd writeFd(mIncFs->openWrite(ifs->control, libFileId));
+        if (writeFd < 0) {
+            LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
+            success = false;
+            break;
+        }
+        const int numBlocks = uncompressedLen / constants().blockSize + 1;
+        std::vector<IncFsDataBlock> instructions;
+        auto remainingData = std::span(libData);
+        for (int i = 0; i < numBlocks - 1; i++) {
+            auto inst = IncFsDataBlock{
+                    .fileFd = writeFd,
+                    .pageIndex = static_cast<IncFsBlockIndex>(i),
+                    .compression = INCFS_COMPRESSION_KIND_NONE,
+                    .kind = INCFS_BLOCK_KIND_DATA,
+                    .dataSize = static_cast<uint16_t>(constants().blockSize),
+                    .data = reinterpret_cast<const char*>(remainingData.data()),
+            };
+            instructions.push_back(inst);
+            remainingData = remainingData.subspan(constants().blockSize);
+        }
+        // Last block
+        auto inst = IncFsDataBlock{
+                .fileFd = writeFd,
+                .pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1),
+                .compression = INCFS_COMPRESSION_KIND_NONE,
+                .kind = INCFS_BLOCK_KIND_DATA,
+                .dataSize = static_cast<uint16_t>(remainingData.size()),
+                .data = reinterpret_cast<const char*>(remainingData.data()),
+        };
+        instructions.push_back(inst);
+        size_t res = mIncFs->writeBlocks(instructions);
+        if (res != instructions.size()) {
+            LOG(ERROR) << "Failed to write data into: " << targetLibPath;
+            success = false;
+        }
+        instructions.clear();
+    }
+    zipFile.get()->endIteration(cookie);
+    return success;
+}
+
 binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
                                                                                   int newStatus) {
     std::unique_lock l(incrementalService.mLock);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index dec9f64..2444dde 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -126,7 +126,8 @@
 
     std::vector<std::string> listFiles(StorageId storage) const;
     bool startLoading(StorageId storage) const;
-
+    bool configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
+                                 std::string_view libDirRelativePath, std::string_view abi);
     class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
     public:
         IncrementalDataLoaderListener(IncrementalService& incrementalService)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 036335c..eef6c63 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
+import android.graphics.GraphicsStatsService;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.ConnectivityModuleConnector;
 import android.net.ITetheringConnector;
diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
index d825b6b..45e0aac 100644
--- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java
@@ -107,7 +107,7 @@
         }
         @Event.EventType int eventType  = CALL_TYPE_TO_EVENT_TYPE.get(callType);
         Event event = new Event.Builder(date, eventType)
-                .setCallDetails(new Event.CallDetails(durationSeconds))
+                .setDurationSeconds((int) durationSeconds)
                 .build();
         mEventConsumer.accept(phoneNumber, event);
         return true;
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index bb97533..b60ed3e 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -35,7 +35,7 @@
  */
 public class ConversationInfo {
 
-    private static final int FLAG_VIP = 1;
+    private static final int FLAG_IMPORTANT = 1;
 
     private static final int FLAG_NOTIFICATION_SILENCED = 1 << 1;
 
@@ -50,7 +50,7 @@
     private static final int FLAG_DEMOTED = 1 << 6;
 
     @IntDef(flag = true, prefix = {"FLAG_"}, value = {
-            FLAG_VIP,
+            FLAG_IMPORTANT,
             FLAG_NOTIFICATION_SILENCED,
             FLAG_BUBBLED,
             FLAG_PERSON_IMPORTANT,
@@ -129,9 +129,9 @@
         return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
     }
 
-    /** Whether this conversation is marked as VIP by the user. */
-    public boolean isVip() {
-        return hasConversationFlags(FLAG_VIP);
+    /** Whether this conversation is marked as important by the user. */
+    public boolean isImportant() {
+        return hasConversationFlags(FLAG_IMPORTANT);
     }
 
     /** Whether the notifications for this conversation should be silenced. */
@@ -208,8 +208,8 @@
         sb.append("]");
         sb.append(", conversationFlags=0x").append(Integer.toHexString(mConversationFlags));
         sb.append(" [");
-        if (isVip()) {
-            sb.append("Vip");
+        if (isImportant()) {
+            sb.append("Imp");
         }
         if (isNotificationSilenced()) {
             sb.append("Sil");
@@ -221,7 +221,7 @@
             sb.append("Dem");
         }
         if (isPersonImportant()) {
-            sb.append("Imp");
+            sb.append("PIm");
         }
         if (isPersonBot()) {
             sb.append("Bot");
@@ -318,8 +318,8 @@
             return this;
         }
 
-        Builder setVip(boolean value) {
-            return setConversationFlag(FLAG_VIP, value);
+        Builder setImportant(boolean value) {
+            return setConversationFlag(FLAG_IMPORTANT, value);
         }
 
         Builder setNotificationSilenced(boolean value) {
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index f17e1b9..3649921 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -40,6 +40,9 @@
     // Phone Number -> Shortcut ID
     private final Map<String, String> mPhoneNumberToShortcutIdMap = new ArrayMap<>();
 
+    // Notification Channel ID -> Shortcut ID
+    private final Map<String, String> mNotifChannelIdToShortcutIdMap = new ArrayMap<>();
+
     void addOrUpdate(@NonNull ConversationInfo conversationInfo) {
         mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo);
 
@@ -57,6 +60,11 @@
         if (phoneNumber != null) {
             mPhoneNumberToShortcutIdMap.put(phoneNumber, conversationInfo.getShortcutId());
         }
+
+        String notifChannelId = conversationInfo.getNotificationChannelId();
+        if (notifChannelId != null) {
+            mNotifChannelIdToShortcutIdMap.put(notifChannelId, conversationInfo.getShortcutId());
+        }
     }
 
     void deleteConversation(@NonNull String shortcutId) {
@@ -79,6 +87,11 @@
         if (phoneNumber != null) {
             mPhoneNumberToShortcutIdMap.remove(phoneNumber);
         }
+
+        String notifChannelId = conversationInfo.getNotificationChannelId();
+        if (notifChannelId != null) {
+            mNotifChannelIdToShortcutIdMap.remove(notifChannelId);
+        }
     }
 
     void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
@@ -106,4 +119,9 @@
     ConversationInfo getConversationByPhoneNumber(@NonNull String phoneNumber) {
         return getConversation(mPhoneNumberToShortcutIdMap.get(phoneNumber));
     }
+
+    @Nullable
+    ConversationInfo getConversationByNotificationChannelId(@NonNull String notifChannelId) {
+        return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId));
+    }
 }
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 43e7738..7a3ed53 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -21,11 +21,11 @@
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Person;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -69,6 +69,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * A class manages the lifecycle of the conversations and associated data, and exposes the methods
@@ -96,7 +97,6 @@
     private final ContentObserver mMmsSmsContentObserver;
 
     private ShortcutServiceInternal mShortcutServiceInternal;
-    private UsageStatsManagerInternal mUsageStatsManagerInternal;
     private ShortcutManager mShortcutManager;
     private UserManager mUserManager;
 
@@ -118,7 +118,6 @@
     /** Initialization. Called when the system services are up running. */
     public void initialize() {
         mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
-        mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
         mShortcutManager = mContext.getSystemService(ShortcutManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
 
@@ -160,8 +159,8 @@
         mNotificationListeners.put(userId, notificationListener);
         try {
             notificationListener.registerAsSystemService(mContext,
-                    new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getSimpleName()),
-                    UserHandle.myUserId());
+                    new ComponentName(PLATFORM_PACKAGE_NAME, getClass().getCanonicalName()),
+                    userId);
         } catch (RemoteException e) {
             // Should never occur for local calls.
         }
@@ -386,36 +385,6 @@
     }
 
     @VisibleForTesting
-    @WorkerThread
-    void queryUsageStatsService(@UserIdInt int userId, long currentTime, long lastQueryTime) {
-        UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
-                userId, lastQueryTime, currentTime, false, false);
-        if (usageEvents == null) {
-            return;
-        }
-        while (usageEvents.hasNextEvent()) {
-            UsageEvents.Event e = new UsageEvents.Event();
-            usageEvents.getNextEvent(e);
-
-            String packageName = e.getPackageName();
-            PackageData packageData = getPackage(packageName, userId);
-            if (packageData == null) {
-                continue;
-            }
-            if (e.getEventType() == UsageEvents.Event.SHORTCUT_INVOCATION) {
-                String shortcutId = e.getShortcutId();
-                if (packageData.getConversationStore().getConversation(shortcutId) != null) {
-                    EventHistoryImpl eventHistory =
-                            packageData.getEventStore().getOrCreateShortcutEventHistory(
-                                    shortcutId);
-                    eventHistory.addEvent(
-                            new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
-                }
-            }
-        }
-    }
-
-    @VisibleForTesting
     ContentObserver getContactsContentObserverForTesting(@UserIdInt int userId) {
         return mContactsContentObservers.get(userId);
     }
@@ -604,6 +573,44 @@
             long currentTime = System.currentTimeMillis();
             eventHistory.addEvent(new Event(currentTime, Event.TYPE_NOTIFICATION_OPENED));
         }
+
+        @Override
+        public void onNotificationChannelModified(String pkg, UserHandle user,
+                NotificationChannel channel, int modificationType) {
+            PackageData packageData = getPackage(pkg, user.getIdentifier());
+            String shortcutId = channel.getConversationId();
+            if (packageData == null || shortcutId == null) {
+                return;
+            }
+            ConversationStore conversationStore = packageData.getConversationStore();
+            ConversationInfo conversationInfo = conversationStore.getConversation(shortcutId);
+            if (conversationInfo == null) {
+                return;
+            }
+            ConversationInfo.Builder builder = new ConversationInfo.Builder(conversationInfo);
+            switch (modificationType) {
+                case NOTIFICATION_CHANNEL_OR_GROUP_ADDED:
+                case NOTIFICATION_CHANNEL_OR_GROUP_UPDATED:
+                    builder.setNotificationChannelId(channel.getId());
+                    builder.setImportant(channel.isImportantConversation());
+                    builder.setDemoted(channel.isDemoted());
+                    builder.setNotificationSilenced(
+                            channel.getImportance() <= NotificationManager.IMPORTANCE_LOW);
+                    builder.setBubbled(channel.canBubble());
+                    break;
+                case NOTIFICATION_CHANNEL_OR_GROUP_DELETED:
+                    // If the notification channel is deleted, revert all the notification settings
+                    // to the default value.
+                    builder.setNotificationChannelId(null);
+                    builder.setImportant(false);
+                    builder.setDemoted(false);
+                    builder.setNotificationSilenced(false);
+                    builder.setBubbled(false);
+                    break;
+            }
+            conversationStore.addOrUpdate(builder.build());
+            // TODO: Cache the shortcut when a conversation's notification setting is changed.
+        }
     }
 
     /**
@@ -612,19 +619,20 @@
      */
     private class UsageStatsQueryRunnable implements Runnable {
 
-        private final int mUserId;
-        private long mLastQueryTime;
+        private final UsageStatsQueryHelper mUsageStatsQueryHelper;
+        private long mLastEventTimestamp;
 
         private UsageStatsQueryRunnable(int userId) {
-            mUserId = userId;
-            mLastQueryTime = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
+            mUsageStatsQueryHelper = mInjector.createUsageStatsQueryHelper(userId,
+                    (packageName) -> getPackage(packageName, userId));
+            mLastEventTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS;
         }
 
         @Override
         public void run() {
-            long currentTime = System.currentTimeMillis();
-            queryUsageStatsService(mUserId, currentTime, mLastQueryTime);
-            mLastQueryTime = currentTime;
+            if (mUsageStatsQueryHelper.querySince(mLastEventTimestamp)) {
+                mLastEventTimestamp = mUsageStatsQueryHelper.getLastEventTimestamp();
+            }
         }
     }
 
@@ -680,6 +688,11 @@
             return new SmsQueryHelper(context, eventConsumer);
         }
 
+        UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
+                Function<String, PackageData> packageDataGetter) {
+            return new UsageStatsQueryHelper(userId, packageDataGetter);
+        }
+
         int getCallingUserId() {
             return Binder.getCallingUserHandle().getIdentifier();
         }
diff --git a/services/people/java/com/android/server/people/data/Event.java b/services/people/java/com/android/server/people/data/Event.java
index c2364a2..81411c0 100644
--- a/services/people/java/com/android/server/people/data/Event.java
+++ b/services/people/java/com/android/server/people/data/Event.java
@@ -18,14 +18,12 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.text.format.DateFormat;
 import android.util.ArraySet;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 import java.util.Set;
 
 /** An event representing the interaction with a specific conversation or app. */
@@ -55,6 +53,8 @@
 
     public static final int TYPE_CALL_MISSED = 12;
 
+    public static final int TYPE_IN_APP_CONVERSATION = 13;
+
     @IntDef(prefix = { "TYPE_" }, value = {
             TYPE_SHORTCUT_INVOCATION,
             TYPE_NOTIFICATION_POSTED,
@@ -68,6 +68,7 @@
             TYPE_CALL_OUTGOING,
             TYPE_CALL_INCOMING,
             TYPE_CALL_MISSED,
+            TYPE_IN_APP_CONVERSATION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
@@ -95,6 +96,7 @@
         CALL_EVENT_TYPES.add(TYPE_CALL_MISSED);
 
         ALL_EVENT_TYPES.add(TYPE_SHORTCUT_INVOCATION);
+        ALL_EVENT_TYPES.add(TYPE_IN_APP_CONVERSATION);
         ALL_EVENT_TYPES.addAll(NOTIFICATION_EVENT_TYPES);
         ALL_EVENT_TYPES.addAll(SHARE_EVENT_TYPES);
         ALL_EVENT_TYPES.addAll(SMS_EVENT_TYPES);
@@ -105,18 +107,18 @@
 
     private final int mType;
 
-    private final CallDetails mCallDetails;
+    private final int mDurationSeconds;
 
     Event(long timestamp, @EventType int type) {
         mTimestamp = timestamp;
         mType = type;
-        mCallDetails = null;
+        mDurationSeconds = 0;
     }
 
     private Event(@NonNull Builder builder) {
         mTimestamp = builder.mTimestamp;
         mType = builder.mType;
-        mCallDetails = builder.mCallDetails;
+        mDurationSeconds = builder.mDurationSeconds;
     }
 
     public long getTimestamp() {
@@ -128,12 +130,35 @@
     }
 
     /**
-     * Gets the {@link CallDetails} of the event. It is only available if the event type is one of
-     * {@code CALL_EVENT_TYPES}, otherwise, it's always {@code null}.
+     * Gets the duration of the event in seconds. It is only available for these events:
+     * <ul>
+     *     <li>{@link #TYPE_CALL_INCOMING}
+     *     <li>{@link #TYPE_CALL_OUTGOING}
+     *     <li>{@link #TYPE_IN_APP_CONVERSATION}
+     * </ul>
+     * <p>For the other event types, it always returns {@code 0}.
      */
-    @Nullable
-    public CallDetails getCallDetails() {
-        return mCallDetails;
+    public int getDurationSeconds() {
+        return mDurationSeconds;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof Event)) {
+            return false;
+        }
+        Event other = (Event) obj;
+        return mTimestamp == other.mTimestamp
+                && mType == other.mType
+                && mDurationSeconds == other.mDurationSeconds;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTimestamp, mType, mDurationSeconds);
     }
 
     @Override
@@ -142,32 +167,13 @@
         sb.append("Event {");
         sb.append("timestamp=").append(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimestamp));
         sb.append(", type=").append(mType);
-        if (mCallDetails != null) {
-            sb.append(", callDetails=").append(mCallDetails);
+        if (mDurationSeconds > 0) {
+            sb.append(", durationSeconds=").append(mDurationSeconds);
         }
         sb.append("}");
         return sb.toString();
     }
 
-    /** Type-specific details of a call event. */
-    public static class CallDetails {
-
-        private final long mDurationSeconds;
-
-        CallDetails(long durationSeconds) {
-            mDurationSeconds = durationSeconds;
-        }
-
-        public long getDurationSeconds() {
-            return mDurationSeconds;
-        }
-
-        @Override
-        public String toString() {
-            return "CallDetails {durationSeconds=" + mDurationSeconds + "}";
-        }
-    }
-
     /** Builder class for {@link Event} objects. */
     static class Builder {
 
@@ -175,16 +181,15 @@
 
         private final int mType;
 
-        private CallDetails mCallDetails;
+        private int mDurationSeconds;
 
         Builder(long timestamp, @EventType int type) {
             mTimestamp = timestamp;
             mType = type;
         }
 
-        Builder setCallDetails(CallDetails callDetails) {
-            Preconditions.checkArgument(CALL_EVENT_TYPES.contains(mType));
-            mCallDetails = callDetails;
+        Builder setDurationSeconds(int durationSeconds) {
+            mDurationSeconds = durationSeconds;
             return this;
         }
 
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
new file mode 100644
index 0000000..4e37f47
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.LocusId;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+
+import com.android.server.LocalServices;
+
+import java.util.Map;
+import java.util.function.Function;
+
+/** A helper class that queries {@link UsageStatsManagerInternal}. */
+class UsageStatsQueryHelper {
+
+    private final UsageStatsManagerInternal mUsageStatsManagerInternal;
+    private final int mUserId;
+    private final Function<String, PackageData> mPackageDataGetter;
+    // Activity name -> Conversation start event (LOCUS_ID_SET)
+    private final Map<ComponentName, UsageEvents.Event> mConvoStartEvents = new ArrayMap<>();
+    private long mLastEventTimestamp;
+
+    /**
+     * @param userId The user whose events are to be queried.
+     * @param packageDataGetter The function to get {@link PackageData} with a package name.
+     */
+    UsageStatsQueryHelper(@UserIdInt int userId,
+            Function<String, PackageData> packageDataGetter) {
+        mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class);
+        mUserId = userId;
+        mPackageDataGetter = packageDataGetter;
+    }
+
+    /**
+     * Queries {@link UsageStatsManagerInternal} for the recent events occurred since {@code
+     * sinceTime} and adds the derived {@link Event}s into the corresponding package's event store,
+     *
+     * @return true if the query runs successfully and at least one event is found.
+     */
+    boolean querySince(long sinceTime) {
+        UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser(
+                mUserId, sinceTime, System.currentTimeMillis(), false, false);
+        if (usageEvents == null) {
+            return false;
+        }
+        boolean hasEvents = false;
+        while (usageEvents.hasNextEvent()) {
+            UsageEvents.Event e = new UsageEvents.Event();
+            usageEvents.getNextEvent(e);
+
+            hasEvents = true;
+            mLastEventTimestamp = Math.max(mLastEventTimestamp, e.getTimeStamp());
+            String packageName = e.getPackageName();
+            PackageData packageData = mPackageDataGetter.apply(packageName);
+            if (packageData == null) {
+                continue;
+            }
+            switch (e.getEventType()) {
+                case UsageEvents.Event.SHORTCUT_INVOCATION:
+                    addEventByShortcutId(packageData, e.getShortcutId(),
+                            new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
+                    break;
+                case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
+                    addEventByNotificationChannelId(packageData, e.getNotificationChannelId(),
+                            new Event(e.getTimeStamp(), Event.TYPE_NOTIFICATION_POSTED));
+                    break;
+                case UsageEvents.Event.LOCUS_ID_SET:
+                    onInAppConversationEnded(packageData, e);
+                    LocusId locusId = e.getLocusId() != null ? new LocusId(e.getLocusId()) : null;
+                    if (locusId != null) {
+                        if (packageData.getConversationStore().getConversationByLocusId(locusId)
+                                != null) {
+                            ComponentName activityName =
+                                    new ComponentName(packageName, e.getClassName());
+                            mConvoStartEvents.put(activityName, e);
+                        }
+                    }
+                    break;
+                case UsageEvents.Event.ACTIVITY_PAUSED:
+                case UsageEvents.Event.ACTIVITY_STOPPED:
+                case UsageEvents.Event.ACTIVITY_DESTROYED:
+                    onInAppConversationEnded(packageData, e);
+                    break;
+            }
+        }
+        return hasEvents;
+    }
+
+    long getLastEventTimestamp() {
+        return mLastEventTimestamp;
+    }
+
+    private void onInAppConversationEnded(@NonNull PackageData packageData,
+            @NonNull UsageEvents.Event endEvent) {
+        ComponentName activityName =
+                new ComponentName(endEvent.getPackageName(), endEvent.getClassName());
+        UsageEvents.Event startEvent = mConvoStartEvents.remove(activityName);
+        if (startEvent == null || startEvent.getTimeStamp() >= endEvent.getTimeStamp()) {
+            return;
+        }
+        long durationMillis = endEvent.getTimeStamp() - startEvent.getTimeStamp();
+        Event event = new Event.Builder(startEvent.getTimeStamp(), Event.TYPE_IN_APP_CONVERSATION)
+                .setDurationSeconds((int) (durationMillis / DateUtils.SECOND_IN_MILLIS))
+                .build();
+        addEventByLocusId(packageData, new LocusId(startEvent.getLocusId()), event);
+    }
+
+    private void addEventByShortcutId(PackageData packageData, String shortcutId, Event event) {
+        if (packageData.getConversationStore().getConversation(shortcutId) == null) {
+            return;
+        }
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
+                shortcutId);
+        eventHistory.addEvent(event);
+    }
+
+    private void addEventByLocusId(PackageData packageData, LocusId locusId, Event event) {
+        if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
+            return;
+        }
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory(
+                locusId);
+        eventHistory.addEvent(event);
+    }
+
+    private void addEventByNotificationChannelId(PackageData packageData,
+            String notificationChannelId, Event event) {
+        ConversationInfo conversationInfo =
+                packageData.getConversationStore().getConversationByNotificationChannelId(
+                        notificationChannelId);
+        if (conversationInfo == null) {
+            return;
+        }
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
+                conversationInfo.getShortcutId());
+        eventHistory.addEvent(event);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 8d2a152..6083ce34 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -174,12 +174,12 @@
         final int app1ConnectiongGroup = 10;
         final int app1UidUser2 = 1010123;
         final int app1PidUser2 = 12347;
-        final int app1Pss1 = 34567;
-        final int app1Rss1 = 45678;
-        final int app1Pss2 = 34568;
-        final int app1Rss2 = 45679;
-        final int app1Pss3 = 34569;
-        final int app1Rss3 = 45680;
+        final long app1Pss1 = 34567;
+        final long app1Rss1 = 45678;
+        final long app1Pss2 = 34568;
+        final long app1Rss2 = 45679;
+        final long app1Pss3 = 34569;
+        final long app1Rss3 = 45680;
         final String app1ProcessName = "com.android.test.stub1:process";
         final String app1PackageName = "com.android.test.stub1";
 
@@ -344,8 +344,8 @@
         // Case 4: Create a process from another package with kill from lmkd
         final int app2UidUser2 = 1010234;
         final int app2PidUser2 = 12348;
-        final int app2Pss1 = 54321;
-        final int app2Rss1 = 65432;
+        final long app2Pss1 = 54321;
+        final long app2Rss1 = 65432;
         final String app2ProcessName = "com.android.test.stub2:process";
         final String app2PackageName = "com.android.test.stub2";
 
@@ -402,8 +402,8 @@
         final int app3UidUser2 = 1010345;
         final int app3PidUser2 = 12349;
         final int app3ConnectiongGroup = 4;
-        final int app3Pss1 = 54320;
-        final int app3Rss1 = 65430;
+        final long app3Pss1 = 54320;
+        final long app3Rss1 = 65430;
         final String app3ProcessName = "com.android.test.stub3:process";
         final String app3PackageName = "com.android.test.stub3";
         final String app3Description = "native crash";
@@ -529,8 +529,8 @@
         final int app3Uid = 10345;
         final int app3IsolatedUid = 99001; // it's an isolated process
         final int app3Pid = 12350;
-        final int app3Pss2 = 23232;
-        final int app3Rss2 = 32323;
+        final long app3Pss2 = 23232;
+        final long app3Rss2 = 32323;
         final String app3Description2 = "force close";
 
         sleep(1);
@@ -618,8 +618,8 @@
 
         sleep(1);
         final int app1IsolatedUidUser2 = 1099002; // isolated uid
-        final int app1Pss4 = 34343;
-        final int app1Rss4 = 43434;
+        final long app1Pss4 = 34343;
+        final long app1Rss4 = 43434;
         final long now8 = System.currentTimeMillis();
         sigNum = OsConstants.SIGKILL;
         doReturn(new Pair<Long, Object>(now8, makeSignalStatus(sigNum)))
@@ -673,8 +673,8 @@
         sleep(1);
         final int app1Pid2User2 = 56565;
         final int app1IsolatedUid2User2 = 1099003; // isolated uid
-        final int app1Pss5 = 34344;
-        final int app1Rss5 = 43435;
+        final long app1Pss5 = 34344;
+        final long app1Rss5 = 43435;
         final long now9 = System.currentTimeMillis();
         sigNum = OsConstants.SIGKILL;
         doReturn(new Pair<Long, Object>(now9, makeSignalStatus(sigNum)))
@@ -831,7 +831,7 @@
     }
 
     private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
-            int connectionGroup, int procState, int pss, int rss,
+            int connectionGroup, int procState, long pss, long rss,
             String processName, String packageName) {
         ApplicationInfo ai = new ApplicationInfo();
         ai.packageName = packageName;
@@ -847,8 +847,8 @@
         app.connectionGroup = connectionGroup;
         app.setProcState = procState;
         app.lastMemInfo = spy(new Debug.MemoryInfo());
-        doReturn(pss).when(app.lastMemInfo).getTotalPss();
-        doReturn(rss).when(app.lastMemInfo).getTotalRss();
+        doReturn((int) pss).when(app.lastMemInfo).getTotalPss();
+        doReturn((int) rss).when(app.lastMemInfo).getTotalRss();
         return app;
     }
 
@@ -856,7 +856,7 @@
             Long timestamp, Integer pid, Integer uid, Integer packageUid,
             Integer definingUid, String processName, Integer connectionGroup,
             Integer reason, Integer subReason, Integer status,
-            Integer pss, Integer rss, Integer importance, String description) {
+            Long pss, Long rss, Integer importance, String description) {
         assertNotNull(info);
 
         if (timestamp != null) {
@@ -892,10 +892,10 @@
             assertEquals(status.intValue(), info.getStatus());
         }
         if (pss != null) {
-            assertEquals(pss.intValue(), info.getPss());
+            assertEquals(pss.longValue(), info.getPss());
         }
         if (rss != null) {
-            assertEquals(rss.intValue(), info.getRss());
+            assertEquals(rss.longValue(), info.getRss());
         }
         if (importance != null) {
             assertEquals(importance.intValue(), info.getImportance());
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 16dde42..3778e17 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -216,8 +216,8 @@
         doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS + 1000)
                 .when(sessionFile1).lastModified();
         final long sessionId1 = 342;
-        final BlobHandle blobHandle1 = mock(BlobHandle.class);
-        doReturn(System.currentTimeMillis() - 1000).when(blobHandle1).getExpiryTimeMillis();
+        final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+                "label1", System.currentTimeMillis() - 1000, "tag1");
         final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
                 sessionId1, sessionFile1, blobHandle1);
         mUserSessions.append(sessionId1, session1);
@@ -226,8 +226,8 @@
         doReturn(System.currentTimeMillis() - 20000)
                 .when(sessionFile2).lastModified();
         final long sessionId2 = 4597;
-        final BlobHandle blobHandle2 = mock(BlobHandle.class);
-        doReturn(System.currentTimeMillis() + 20000).when(blobHandle2).getExpiryTimeMillis();
+        final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+                "label2", System.currentTimeMillis() + 20000, "tag2");
         final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
                 sessionId2, sessionFile2, blobHandle2);
         mUserSessions.append(sessionId2, session2);
@@ -236,8 +236,8 @@
         doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS - 2000)
                 .when(sessionFile3).lastModified();
         final long sessionId3 = 9484;
-        final BlobHandle blobHandle3 = mock(BlobHandle.class);
-        doReturn(System.currentTimeMillis() + 30000).when(blobHandle3).getExpiryTimeMillis();
+        final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(),
+                "label3", System.currentTimeMillis() + 30000, "tag3");
         final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
                 sessionId3, sessionFile3, blobHandle3);
         mUserSessions.append(sessionId3, session3);
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
index 8e9d7ee..3566aee 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java
@@ -21,6 +21,7 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 
+import org.junit.AssumptionViolatedException;
 import org.junit.rules.TestRule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
@@ -100,6 +101,11 @@
             }
 
             @Override
+            protected void skipped(AssumptionViolatedException e, Description description) {
+                tearDown(e);
+            }
+
+            @Override
             protected void failed(Throwable e, Description description) {
                 tearDown(e);
             }
diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
index b7e71de..8e0ccf0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java
@@ -16,8 +16,10 @@
 
 package com.android.server.testables;
 
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
@@ -30,6 +32,7 @@
 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
 
 import org.junit.After;
+import org.junit.AssumptionViolatedException;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.Description;
@@ -57,6 +60,8 @@
     @Mock private Supplier<StaticMockFixture> mSupplyA;
     @Mock private Supplier<StaticMockFixture> mSupplyB;
     @Mock private Statement mStatement;
+    @Mock private Statement mSkipStatement;
+    @Mock private Statement mThrowStatement;
     @Mock private Description mDescription;
 
     @Before
@@ -91,17 +96,22 @@
         when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0));
         doNothing().when(mB1).setUpMockBehaviors();
         doNothing().when(mStatement).evaluate();
+        doThrow(new AssumptionViolatedException("bad assumption, test should be skipped"))
+                .when(mSkipStatement).evaluate();
+        doThrow(new IllegalArgumentException("bad argument, test should be failed"))
+                .when(mThrowStatement).evaluate();
         doNothing().when(mA1).tearDown();
         doNothing().when(mB1).tearDown();
     }
 
     private InOrder mocksInOrder()  {
-        return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB,
-                mA1, mA2, mB1, mB2, mStatement, mDescription);
+        return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, mA1, mA2, mB1, mB2,
+                mStatement, mSkipStatement, mThrowStatement, mDescription);
     }
 
     private void verifyNoMoreImportantMockInteractions()  {
-        verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement);
+        verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement,
+                mSkipStatement, mThrowStatement);
     }
 
     @Test
@@ -183,4 +193,66 @@
 
         verifyNoMoreImportantMockInteractions();
     }
+
+    @Test
+    public void testTearDownOnSkippedTests() throws Throwable {
+        InOrder inOrder = mocksInOrder();
+
+        StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
+            @Override public StaticMockitoSessionBuilder getSessionBuilder() {
+                return mSessionBuilder;
+            }
+        };
+        Statement skipStatement = rule.apply(mSkipStatement, mDescription);
+
+        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
+        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
+        inOrder.verify(mA1).setUpMockBehaviors();
+        inOrder.verify(mB1).setUpMockBehaviors();
+
+        try {
+            skipStatement.evaluate();
+            fail("AssumptionViolatedException should have been thrown");
+        } catch (AssumptionViolatedException e) {
+            // expected
+        }
+
+        inOrder.verify(mSkipStatement).evaluate();
+        // note: tearDown in reverse order
+        inOrder.verify(mB1).tearDown();
+        inOrder.verify(mA1).tearDown();
+
+        verifyNoMoreImportantMockInteractions();
+    }
+
+    @Test
+    public void testTearDownOnFailedTests() throws Throwable {
+        InOrder inOrder = mocksInOrder();
+
+        StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) {
+            @Override public StaticMockitoSessionBuilder getSessionBuilder() {
+                return mSessionBuilder;
+            }
+        };
+        Statement failStatement = rule.apply(mThrowStatement, mDescription);
+
+        inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
+        inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class));
+        inOrder.verify(mA1).setUpMockBehaviors();
+        inOrder.verify(mB1).setUpMockBehaviors();
+
+        try {
+            failStatement.evaluate();
+            fail("IllegalArgumentException should have been thrown");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        inOrder.verify(mThrowStatement).evaluate();
+        // note: tearDown in reverse order
+        inOrder.verify(mB1).tearDown();
+        inOrder.verify(mA1).tearDown();
+
+        verifyNoMoreImportantMockInteractions();
+    }
 }
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 cf10559..dfe950e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -47,6 +47,7 @@
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.nullValue;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -703,14 +704,20 @@
 
     @Test
     public void takeScreenshot_returnNull() {
-        // no canTakeScreenshot, should return null.
-        when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
-        assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
-
         // no checkAccessibilityAccess, should return null.
         when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true);
         when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false);
-        assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue()));
+        mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> {
+            assertNull(result);
+        }));
+    }
+
+    @Test (expected = SecurityException.class)
+    public void takeScreenshot_throwSecurityException() {
+        // no canTakeScreenshot, should throw security exception.
+        when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false);
+        mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> {
+        }));
     }
 
     private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType,
@@ -852,8 +859,5 @@
 
         @Override
         public void onFingerprintGesture(int gesture) {}
-
-        @Override
-        public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {}
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index d38c80c..6aa9287 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +33,9 @@
 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.iris.IIrisService;
 import android.os.Binder;
 import android.os.Bundle;
 
@@ -61,6 +65,12 @@
     AuthService.Injector mInjector;
     @Mock
     IBiometricService mBiometricService;
+    @Mock
+    IFingerprintService mFingerprintService;
+    @Mock
+    IIrisService mIrisService;
+    @Mock
+    IFaceService mFaceService;
 
     @Before
     public void setUp() {
@@ -76,10 +86,28 @@
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mInjector.getBiometricService()).thenReturn(mBiometricService);
         when(mInjector.getConfiguration(any())).thenReturn(config);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
-                .thenReturn(true);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(true);
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+        when(mInjector.getFingerprintService()).thenReturn(mFingerprintService);
+        when(mInjector.getFaceService()).thenReturn(mFaceService);
+        when(mInjector.getIrisService()).thenReturn(mIrisService);
+    }
+
+    @Test
+    public void testRegisterNullService_doesNotRegister() throws Exception {
+
+        // Config contains Fingerprint, Iris, Face, but services are all null
+
+        when(mInjector.getFingerprintService()).thenReturn(null);
+        when(mInjector.getFaceService()).thenReturn(null);
+        when(mInjector.getIrisService()).thenReturn(null);
+
+        mAuthService = new AuthService(mContext, mInjector);
+        mAuthService.onStart();
+
+        verify(mBiometricService, never()).registerAuthenticator(
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                any());
     }
 
 
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 4a7636a..c9ec874 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -423,7 +423,8 @@
         PackageInfo packageInfo =
                 mRealContext
                         .getPackageManager()
-                        .getPackageInfo(TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNATURES);
+                        .getPackageInfo(TEST_FRAMEWORK_PACKAGE,
+                                PackageManager.GET_SIGNING_CERTIFICATES);
         doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt());
         doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt());
         return makeVerificationIntent(INSTALLER);
diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
index 7a16d17..a545010 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java
@@ -92,7 +92,7 @@
         assertEquals(1, events.size());
         assertEquals(Event.TYPE_CALL_INCOMING, events.get(0).getType());
         assertEquals(100L, events.get(0).getTimestamp());
-        assertEquals(30L, events.get(0).getCallDetails().getDurationSeconds());
+        assertEquals(30L, events.get(0).getDurationSeconds());
     }
 
     @Test
@@ -108,7 +108,7 @@
         assertEquals(1, events.size());
         assertEquals(Event.TYPE_CALL_OUTGOING, events.get(0).getType());
         assertEquals(100L, events.get(0).getTimestamp());
-        assertEquals(40L, events.get(0).getCallDetails().getDurationSeconds());
+        assertEquals(40L, events.get(0).getDurationSeconds());
     }
 
     @Test
@@ -124,7 +124,7 @@
         assertEquals(1, events.size());
         assertEquals(Event.TYPE_CALL_MISSED, events.get(0).getType());
         assertEquals(100L, events.get(0).getTimestamp());
-        assertEquals(0L, events.get(0).getCallDetails().getDurationSeconds());
+        assertEquals(0L, events.get(0).getDurationSeconds());
     }
 
     @Test
@@ -145,7 +145,7 @@
         assertEquals(100L, events.get(0).getTimestamp());
         assertEquals(Event.TYPE_CALL_OUTGOING, events.get(1).getType());
         assertEquals(110L, events.get(1).getTimestamp());
-        assertEquals(40L, events.get(1).getCallDetails().getDurationSeconds());
+        assertEquals(40L, events.get(1).getDurationSeconds());
     }
 
     private class EventConsumer implements BiConsumer<String, Event> {
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index 05a9a80..c0e7927 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -47,7 +47,7 @@
                 .setContactPhoneNumber(PHONE_NUMBER)
                 .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
                 .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
-                .setVip(true)
+                .setImportant(true)
                 .setNotificationSilenced(true)
                 .setBubbled(true)
                 .setDemoted(true)
@@ -62,7 +62,7 @@
         assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
         assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
         assertTrue(conversationInfo.isShortcutLongLived());
-        assertTrue(conversationInfo.isVip());
+        assertTrue(conversationInfo.isImportant());
         assertTrue(conversationInfo.isNotificationSilenced());
         assertTrue(conversationInfo.isBubbled());
         assertTrue(conversationInfo.isDemoted());
@@ -83,7 +83,7 @@
         assertNull(conversationInfo.getContactPhoneNumber());
         assertNull(conversationInfo.getNotificationChannelId());
         assertFalse(conversationInfo.isShortcutLongLived());
-        assertFalse(conversationInfo.isVip());
+        assertFalse(conversationInfo.isImportant());
         assertFalse(conversationInfo.isNotificationSilenced());
         assertFalse(conversationInfo.isBubbled());
         assertFalse(conversationInfo.isDemoted());
@@ -101,7 +101,7 @@
                 .setContactPhoneNumber(PHONE_NUMBER)
                 .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
                 .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
-                .setVip(true)
+                .setImportant(true)
                 .setNotificationSilenced(true)
                 .setBubbled(true)
                 .setPersonImportant(true)
@@ -110,7 +110,7 @@
                 .build();
 
         ConversationInfo destination = new ConversationInfo.Builder(source)
-                .setVip(false)
+                .setImportant(false)
                 .setContactStarred(false)
                 .build();
 
@@ -120,7 +120,7 @@
         assertEquals(PHONE_NUMBER, destination.getContactPhoneNumber());
         assertEquals(NOTIFICATION_CHANNEL_ID, destination.getNotificationChannelId());
         assertTrue(destination.isShortcutLongLived());
-        assertFalse(destination.isVip());
+        assertFalse(destination.isImportant());
         assertTrue(destination.isNotificationSilenced());
         assertTrue(destination.isBubbled());
         assertTrue(destination.isPersonImportant());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index a40c6ab..331ad59 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -37,6 +37,7 @@
 public final class ConversationStoreTest {
 
     private static final String SHORTCUT_ID = "abc";
+    private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
     private static final LocusId LOCUS_ID = new LocusId("def");
     private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
     private static final String PHONE_NUMBER = "+1234567890";
@@ -59,16 +60,19 @@
 
     @Test
     public void testUpdateConversation() {
-        ConversationInfo original =
-                buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+        ConversationInfo original = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, null);
         mConversationStore.addOrUpdate(original);
         assertEquals(LOCUS_ID, mConversationStore.getConversation(SHORTCUT_ID).getLocusId());
+        assertNull(mConversationStore.getConversation(SHORTCUT_ID).getNotificationChannelId());
 
         LocusId newLocusId = new LocusId("ghi");
         ConversationInfo update = buildConversationInfo(
-                SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER);
+                SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
         mConversationStore.addOrUpdate(update);
-        assertEquals(newLocusId, mConversationStore.getConversation(SHORTCUT_ID).getLocusId());
+        ConversationInfo updated = mConversationStore.getConversation(SHORTCUT_ID);
+        assertEquals(newLocusId, updated.getLocusId());
+        assertEquals(NOTIFICATION_CHANNEL_ID, updated.getNotificationChannelId());
     }
 
     @Test
@@ -97,8 +101,8 @@
 
     @Test
     public void testGetConversationByLocusId() {
-        ConversationInfo in =
-                buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+        ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
         mConversationStore.addOrUpdate(in);
         ConversationInfo out = mConversationStore.getConversationByLocusId(LOCUS_ID);
         assertNotNull(out);
@@ -110,8 +114,8 @@
 
     @Test
     public void testGetConversationByContactUri() {
-        ConversationInfo in =
-                buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+        ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
         mConversationStore.addOrUpdate(in);
         ConversationInfo out = mConversationStore.getConversationByContactUri(CONTACT_URI);
         assertNotNull(out);
@@ -123,8 +127,8 @@
 
     @Test
     public void testGetConversationByPhoneNumber() {
-        ConversationInfo in =
-                buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER);
+        ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
         mConversationStore.addOrUpdate(in);
         ConversationInfo out = mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER);
         assertNotNull(out);
@@ -134,19 +138,36 @@
         assertNull(mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER));
     }
 
+    @Test
+    public void testGetConversationByNotificationChannelId() {
+        ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+        mConversationStore.addOrUpdate(in);
+        ConversationInfo out = mConversationStore.getConversationByNotificationChannelId(
+                NOTIFICATION_CHANNEL_ID);
+        assertNotNull(out);
+        assertEquals(SHORTCUT_ID, out.getShortcutId());
+
+        mConversationStore.deleteConversation(SHORTCUT_ID);
+        assertNull(
+                mConversationStore.getConversationByNotificationChannelId(NOTIFICATION_CHANNEL_ID));
+    }
+
     private static ConversationInfo buildConversationInfo(String shortcutId) {
-        return buildConversationInfo(shortcutId, null, null, null);
+        return buildConversationInfo(shortcutId, null, null, null, null);
     }
 
     private static ConversationInfo buildConversationInfo(
-            String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber) {
+            String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber,
+            String notificationChannelId) {
         return new ConversationInfo.Builder()
                 .setShortcutId(shortcutId)
                 .setLocusId(locusId)
                 .setContactUri(contactUri)
                 .setContactPhoneNumber(phoneNumber)
+                .setNotificationChannelId(notificationChannelId)
                 .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
-                .setVip(true)
+                .setImportant(true)
                 .setBubbled(true)
                 .build();
     }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 9d2091a..498d888 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -16,15 +16,17 @@
 
 package com.android.server.people.data;
 
-import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
+import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -36,11 +38,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
 import android.app.Person;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
 import android.app.prediction.AppTargetId;
-import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -92,6 +95,7 @@
     private static final String TEST_SHORTCUT_ID = "sc";
     private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
     private static final String PHONE_NUMBER = "+1234567890";
+    private static final String NOTIFICATION_CHANNEL_ID = "test : sc";
     private static final long MILLIS_PER_MINUTE = 1000L * 60L;
 
     @Mock private Context mContext;
@@ -107,6 +111,7 @@
     @Mock private StatusBarNotification mStatusBarNotification;
     @Mock private Notification mNotification;
 
+    private NotificationChannel mNotificationChannel;
     private DataManager mDataManager;
     private int mCallingUserId;
     private TestInjector mInjector;
@@ -156,6 +161,10 @@
         when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
         when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
 
+        mNotificationChannel = new NotificationChannel(
+                NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
+        mNotificationChannel.setConversationId("test", TEST_SHORTCUT_ID);
+
         mCallingUserId = USER_ID_PRIMARY;
 
         mInjector = new TestInjector();
@@ -288,9 +297,8 @@
     }
 
     @Test
-    public void testNotificationListener() {
+    public void testNotificationOpened() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
-        mDataManager.onUserUnlocked(USER_ID_SECONDARY);
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
@@ -312,32 +320,80 @@
     }
 
     @Test
-    public void testQueryUsageStatsService() {
-        UsageEvents.Event e = new UsageEvents.Event(SHORTCUT_INVOCATION,
-                System.currentTimeMillis());
-        e.mPackage = TEST_PKG_NAME;
-        e.mShortcutId = TEST_SHORTCUT_ID;
-        List<UsageEvents.Event> events = new ArrayList<>();
-        events.add(e);
-        UsageEvents usageEvents = new UsageEvents(events, new String[]{});
-        when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
-                anyBoolean(), anyBoolean())).thenReturn(usageEvents);
-
+    public void testNotificationChannelCreated() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        mDataManager.onUserUnlocked(USER_ID_SECONDARY);
 
         ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
                 buildPerson());
         mDataManager.onShortcutAddedOrUpdated(shortcut);
 
-        mDataManager.queryUsageStatsService(USER_ID_PRIMARY, 0L, Long.MAX_VALUE);
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+        listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+                mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
 
-        List<Range<Long>> activeShortcutInvocationTimeSlots = new ArrayList<>();
-        mDataManager.forAllPackages(packageData ->
-                activeShortcutInvocationTimeSlots.addAll(
-                        packageData.getEventHistory(TEST_SHORTCUT_ID)
-                                .getEventIndex(Event.TYPE_SHORTCUT_INVOCATION)
-                                .getActiveTimeSlots()));
-        assertEquals(1, activeShortcutInvocationTimeSlots.size());
+        ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+                .getConversationStore()
+                .getConversation(TEST_SHORTCUT_ID);
+        assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+        assertFalse(conversationInfo.isImportant());
+        assertFalse(conversationInfo.isNotificationSilenced());
+        assertFalse(conversationInfo.isDemoted());
+    }
+
+    @Test
+    public void testNotificationChannelModified() {
+        mNotificationChannel.setImportantConversation(true);
+
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        mDataManager.onUserUnlocked(USER_ID_SECONDARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+        listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+                mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+                .getConversationStore()
+                .getConversation(TEST_SHORTCUT_ID);
+        assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+        assertTrue(conversationInfo.isImportant());
+        assertFalse(conversationInfo.isNotificationSilenced());
+        assertFalse(conversationInfo.isDemoted());
+    }
+
+    @Test
+    public void testNotificationChannelDeleted() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        mDataManager.onUserUnlocked(USER_ID_SECONDARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+        listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+                mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
+        ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+                .getConversationStore()
+                .getConversation(TEST_SHORTCUT_ID);
+        assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
+
+        listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+                mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+        conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
+                .getConversationStore()
+                .getConversation(TEST_SHORTCUT_ID);
+        assertNull(conversationInfo.getNotificationChannelId());
+        assertFalse(conversationInfo.isImportant());
+        assertFalse(conversationInfo.isNotificationSilenced());
+        assertFalse(conversationInfo.isDemoted());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
new file mode 100644
index 0000000..e4248a0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.LocusId;
+
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+@RunWith(JUnit4.class)
+public final class UsageStatsQueryHelperTest {
+
+    private static final int USER_ID_PRIMARY = 0;
+    private static final String PKG_NAME = "pkg";
+    private static final String ACTIVITY_NAME = "TestActivity";
+    private static final String SHORTCUT_ID = "abc";
+    private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
+    private static final LocusId LOCUS_ID_1 = new LocusId("locus_1");
+    private static final LocusId LOCUS_ID_2 = new LocusId("locus_2");
+
+    @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+
+    private TestPackageData mPackageData;
+    private UsageStatsQueryHelper mHelper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
+
+        mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false);
+        mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
+                .setShortcutId(SHORTCUT_ID)
+                .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
+                .setLocusId(LOCUS_ID_1)
+                .build();
+
+        mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData);
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+    }
+
+    @Test
+    public void testQueryNoEvents() {
+        assertFalse(mHelper.querySince(50L));
+    }
+
+    @Test
+    public void testQueryShortcutInvocationEvent() {
+        addUsageEvents(createShortcutInvocationEvent(100L));
+
+        assertTrue(mHelper.querySince(50L));
+        assertEquals(100L, mHelper.getLastEventTimestamp());
+        Event expectedEvent = new Event(100L, Event.TYPE_SHORTCUT_INVOCATION);
+        List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents(
+                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(1, events.size());
+        assertEquals(expectedEvent, events.get(0));
+    }
+
+    @Test
+    public void testQueryNotificationInterruptionEvent() {
+        addUsageEvents(createNotificationInterruptionEvent(100L));
+
+        assertTrue(mHelper.querySince(50L));
+        assertEquals(100L, mHelper.getLastEventTimestamp());
+        Event expectedEvent = new Event(100L, Event.TYPE_NOTIFICATION_POSTED);
+        List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents(
+                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(1, events.size());
+        assertEquals(expectedEvent, events.get(0));
+    }
+
+    @Test
+    public void testInAppConversationSwitch() {
+        addUsageEvents(
+                createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+                createLocusIdSetEvent(110_000L, LOCUS_ID_2.getId()));
+
+        assertTrue(mHelper.querySince(50_000L));
+        assertEquals(110_000L, mHelper.getLastEventTimestamp());
+        List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(1, events.size());
+        assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+    }
+
+    @Test
+    public void testInAppConversationExplicitlyEnd() {
+        addUsageEvents(
+                createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+                createLocusIdSetEvent(110_000L, null));
+
+        assertTrue(mHelper.querySince(50_000L));
+        assertEquals(110_000L, mHelper.getLastEventTimestamp());
+        List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(1, events.size());
+        assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+    }
+
+    @Test
+    public void testInAppConversationImplicitlyEnd() {
+        addUsageEvents(
+                createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+                createActivityStoppedEvent(110_000L));
+
+        assertTrue(mHelper.querySince(50_000L));
+        assertEquals(110_000L, mHelper.getLastEventTimestamp());
+        List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(1, events.size());
+        assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+    }
+
+    @Test
+    public void testMultipleInAppConversations() {
+        addUsageEvents(
+                createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
+                createLocusIdSetEvent(110_000L, LOCUS_ID_2.getId()),
+                createLocusIdSetEvent(130_000L, LOCUS_ID_1.getId()),
+                createActivityStoppedEvent(160_000L));
+
+        assertTrue(mHelper.querySince(50_000L));
+        assertEquals(160_000L, mHelper.getLastEventTimestamp());
+        List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents(
+                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(3, events.size());
+        assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0));
+        assertEquals(createInAppConversationEvent(110_000L, 20), events.get(1));
+        assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2));
+    }
+
+    private void addUsageEvents(UsageEvents.Event ... events) {
+        UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{});
+        when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
+                anyBoolean(), anyBoolean())).thenReturn(usageEvents);
+    }
+
+    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+        LocalServices.removeServiceForTest(clazz);
+        LocalServices.addService(clazz, mock);
+    }
+
+    private static UsageEvents.Event createShortcutInvocationEvent(long timestamp) {
+        UsageEvents.Event e = createUsageEvent(UsageEvents.Event.SHORTCUT_INVOCATION, timestamp);
+        e.mShortcutId = SHORTCUT_ID;
+        return e;
+    }
+
+    private static UsageEvents.Event createNotificationInterruptionEvent(long timestamp) {
+        UsageEvents.Event e = createUsageEvent(UsageEvents.Event.NOTIFICATION_INTERRUPTION,
+                timestamp);
+        e.mNotificationChannelId = NOTIFICATION_CHANNEL_ID;
+        return e;
+    }
+
+    private static UsageEvents.Event createLocusIdSetEvent(long timestamp, String locusId) {
+        UsageEvents.Event e = createUsageEvent(UsageEvents.Event.LOCUS_ID_SET, timestamp);
+        e.mClass = ACTIVITY_NAME;
+        e.mLocusId = locusId;
+        return e;
+    }
+
+    private static UsageEvents.Event createActivityStoppedEvent(long timestamp) {
+        UsageEvents.Event e = createUsageEvent(UsageEvents.Event.ACTIVITY_STOPPED, timestamp);
+        e.mClass = ACTIVITY_NAME;
+        return e;
+    }
+
+    private static UsageEvents.Event createUsageEvent(int eventType, long timestamp) {
+        UsageEvents.Event e = new UsageEvents.Event(eventType, timestamp);
+        e.mPackage = PKG_NAME;
+        return e;
+    }
+
+    private static Event createInAppConversationEvent(long timestamp, int durationSeconds) {
+        return new Event.Builder(timestamp, Event.TYPE_IN_APP_CONVERSATION)
+                .setDurationSeconds(durationSeconds)
+                .build();
+    }
+
+    private static class TestConversationStore extends ConversationStore {
+
+        private ConversationInfo mConversationInfo;
+
+        @Override
+        @Nullable
+        ConversationInfo getConversation(@Nullable String shortcutId) {
+            return mConversationInfo;
+        }
+    }
+
+    private static class TestPackageData extends PackageData {
+
+        private final TestConversationStore mConversationStore = new TestConversationStore();
+        private final TestEventStore mEventStore = new TestEventStore();
+
+        TestPackageData(@NonNull String packageName, @UserIdInt int userId,
+                @NonNull Predicate<String> isDefaultDialerPredicate,
+                @NonNull Predicate<String> isDefaultSmsAppPredicate) {
+            super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate);
+        }
+
+        @Override
+        @NonNull
+        ConversationStore getConversationStore() {
+            return mConversationStore;
+        }
+
+        @Override
+        @NonNull
+        EventStore getEventStore() {
+            return mEventStore;
+        }
+    }
+
+    private static class TestEventStore extends EventStore {
+
+        private final EventHistoryImpl mShortcutEventHistory = new TestEventHistoryImpl();
+        private final EventHistoryImpl mLocusEventHistory = new TestEventHistoryImpl();
+
+        @Override
+        @NonNull
+        EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
+            return mShortcutEventHistory;
+        }
+
+        @Override
+        @NonNull
+        EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
+            return mLocusEventHistory;
+        }
+    }
+
+    private static class TestEventHistoryImpl extends EventHistoryImpl {
+
+        private final List<Event> mEvents = new ArrayList<>();
+
+        @Override
+        @NonNull
+        public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) {
+            return mEvents;
+        }
+
+        @Override
+        void addEvent(Event event) {
+            mEvents.add(event);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 8961b53..df2b3ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -557,6 +557,11 @@
         void injectRestoreCallingIdentity(long token) {
             mInjectedCallingUid = (int) token;
         }
+
+        @Override
+        boolean injectHasAccessShortcutsPermission(int callingPid, int callingUid) {
+            return true;
+        }
     }
 
     protected class LauncherAppsTestable extends LauncherApps {
@@ -1618,6 +1623,22 @@
     }
 
     /**
+     * Make a long lived shortcut with an ID.
+     */
+    protected ShortcutInfo makeLongLivedShortcut(String id) {
+        final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, id)
+                .setActivity(new ComponentName(mClientContext.getPackageName(), "main"))
+                .setShortLabel("title-" + id)
+                .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class))
+                .setLongLived(true);
+        final ShortcutInfo s = b.build();
+
+        s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+        return s;
+    }
+
+    /**
      * Make an intent.
      */
     protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 63f667f..2936bdd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -1240,7 +1240,7 @@
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
-                    makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+                    makeLongLivedShortcut("s1"), makeLongLivedShortcut("s2"), makeShortcut("s3"))));
         });
 
         // Pin 2 and 3
@@ -1250,9 +1250,12 @@
         });
 
         // Cache 1 and 2
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2"),
+                    HANDLE_USER_0);
+        });
+
         setCaller(CALLING_PACKAGE_1);
-        getCallerShortcut("s1").setCached();
-        getCallerShortcut("s2").setCached();
 
         // Get manifest shortcuts
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_MANIFEST),
@@ -1315,8 +1318,9 @@
 
     public void testCachedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeShortcut("s2"),
-                    makeShortcut("s3"), makeShortcut("s4"))));
+            assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
+                    makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
+                    makeLongLivedShortcut("s4"))));
         });
 
         // Pin s2
@@ -1325,11 +1329,13 @@
                     HANDLE_USER_0);
         });
 
-        // Cache 2, 3 and 4
+        // Cache some, but non long lived shortcuts will be ignored.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s4"),
+                    HANDLE_USER_0);
+        });
+
         setCaller(CALLING_PACKAGE_1);
-        getCallerShortcut("s2").setCached();
-        getCallerShortcut("s3").setCached();
-        getCallerShortcut("s4").setCached();
 
         // Get dynamic shortcuts
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
@@ -1339,27 +1345,37 @@
                 "s2");
         // Get cached shortcuts
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
-                "s2", "s3", "s4");
+                "s2", "s4");
 
         // Remove a dynamic cached shortcut
-        mManager.removeDynamicShortcuts(list("s3"));
+        mManager.removeDynamicShortcuts(list("s4"));
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
-                "s1", "s2", "s4");
+                "s1", "s2", "s3");
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
-                "s2", "s3", "s4");
+                "s2", "s4");
 
-        // Remove dynamic cached long lived shortcuts
-        mManager.removeLongLivedShortcuts(list("s3", "s4"));
-        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
-                "s1", "s2");
+        // uncache a non-dynamic shortcut. Should be removed.
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.uncacheShortcuts(CALLING_PACKAGE_1, list("s4"),
+                    HANDLE_USER_0);
+        });
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
                 "s2");
 
+        // Cache another shortcut
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s3"),
+                    HANDLE_USER_0);
+        });
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+                "s2", "s3");
+
         // Remove a dynamic cached pinned long lived shortcut
         mManager.removeLongLivedShortcuts(list("s2"));
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
-                "s1");
-        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED));
+                "s1", "s3");
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+                "s3");
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_PINNED),
                 "s2");
     }
@@ -1371,7 +1387,7 @@
         // Set up shortcuts.
 
         setCaller(CALLING_PACKAGE_1);
-        final ShortcutInfo s1_1 = makeShortcut("s1");
+        final ShortcutInfo s1_1 = makeLongLivedShortcut("s1");
         final ShortcutInfo s1_2 = makeShortcutWithLocusId("s2", makeLocusId("l1"));
 
         assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
@@ -1395,6 +1411,8 @@
 
         setCaller(CALLING_PACKAGE_3);
         final ShortcutInfo s3_2 = makeShortcutWithLocusId("s3", makeLocusId("l2"));
+        s3_2.setLongLived();
+
         assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
 
         getCallerShortcut("s3").setTimestamp(START_TIME + 5000);
@@ -1535,26 +1553,20 @@
 
         // TODO More tests: pinned but dynamic.
 
-        // Cache some shortcuts
-        setCaller(CALLING_PACKAGE_1);
-        getCallerShortcut("s1").setCached();
-
-        setCaller(CALLING_PACKAGE_2);
-        getCallerShortcut("s4").setCached();
-
-        setCaller(CALLING_PACKAGE_3);
-        getCallerShortcut("s3").setCached();
-
         setCaller(LAUNCHER_1);
 
+        // Cache some shortcuts. Only long lived shortcuts can get cached.
+        mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1"), getCallingUser());
+        mLauncherApps.cacheShortcuts(CALLING_PACKAGE_3, list("s3"), getCallingUser());
+
         // Cached ones only
         assertShortcutIds(assertAllNotKeyFieldsOnly(
                 mLauncherApps.getShortcuts(buildQuery(
-                        /* time =*/ 0, CALLING_PACKAGE_2,
+                        /* time =*/ 0, CALLING_PACKAGE_3,
                         /* activity =*/ null,
                         ShortcutQuery.FLAG_MATCH_CACHED),
                         getCallingUser())),
-                "s4");
+                "s3");
 
         // All packages.
         assertShortcutIds(assertAllNotKeyFieldsOnly(
@@ -1563,7 +1575,7 @@
                         /* activity =*/ null,
                         ShortcutQuery.FLAG_MATCH_CACHED),
                         getCallingUser())),
-                "s1", "s4", "s3");
+                "s1", "s3");
 
         assertExpectException(
                 IllegalArgumentException.class, "package name must also be set", () -> {
@@ -1581,7 +1593,7 @@
                         /* activity =*/ null,
                         ShortcutQuery.FLAG_MATCH_CACHED),
                         getCallingUser())),
-                "s1", "s4", "s3");
+                "s1", "s3");
     }
 
     public void testGetShortcuts_shortcutKinds() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
index ba493d4..d1c9643 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/WatchdogRollbackLoggerTest.java
@@ -20,7 +20,11 @@
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -36,6 +40,8 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.List;
+
 @RunWith(JUnit4.class)
 public class WatchdogRollbackLoggerTest {
 
@@ -46,6 +52,11 @@
     private PackageInfo mPackageInfo;
 
     private static final String LOGGING_PARENT_KEY = "android.content.pm.LOGGING_PARENT";
+    private static final String LOGGING_PARENT_VALUE = "logging.parent";
+    private static final int PACKAGE_INFO_FLAGS = PackageManager.MATCH_APEX
+            | PackageManager.GET_META_DATA;
+    private static final List<String> sFailingPackages =
+            List.of("package1", "package2", "package3");
 
     @Before
     public void setUp() {
@@ -64,10 +75,12 @@
      */
     @Test
     public void testLogPackageHasNoMetadata() throws Exception {
-        when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
+        when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
         assertThat(logPackage).isNull();
+        verify(mMockPm, times(1)).getPackageInfo(
+                sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
     }
 
     /**
@@ -76,12 +89,16 @@
      */
     @Test
     public void testLogPackageParentKeyIsNull() throws Exception {
-        when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
+        when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
         Bundle bundle = new Bundle();
         bundle.putString(LOGGING_PARENT_KEY, null);
+        mApplicationInfo.metaData = bundle;
+        mPackageInfo.applicationInfo = mApplicationInfo;
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
         assertThat(logPackage).isNull();
+        verify(mMockPm, times(1)).getPackageInfo(
+                sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
     }
 
     /**
@@ -90,15 +107,18 @@
     @Test
     public void testLogPackageHasParentKey() throws Exception {
         Bundle bundle = new Bundle();
-        bundle.putString(LOGGING_PARENT_KEY, "logging.parent");
+        bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
         mApplicationInfo.metaData = bundle;
+        mPackageInfo.applicationInfo = mApplicationInfo;
         mPackageInfo.setLongVersionCode(12345L);
-        when(mMockPm.getApplicationInfo(anyString(), anyInt())).thenReturn(mApplicationInfo);
         when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
-        VersionedPackage expectedLogPackage = new VersionedPackage("logging.parent", 12345);
+        VersionedPackage expectedLogPackage = new VersionedPackage(LOGGING_PARENT_VALUE, 12345);
         assertThat(logPackage).isEqualTo(expectedLogPackage);
+        verify(mMockPm, times(1)).getPackageInfo(
+                sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
+
     }
 
     /**
@@ -107,12 +127,64 @@
     @Test
     public void testLogPackageNameNotFound() throws Exception {
         Bundle bundle = new Bundle();
-        bundle.putString("android.content.pm.LOGGING_PARENT", "logging.parent");
+        bundle.putString(LOGGING_PARENT_KEY, LOGGING_PARENT_VALUE);
         mApplicationInfo.metaData = bundle;
-        when(mMockPm.getPackageInfo(anyString(), anyInt())).thenThrow(
+        mPackageInfo.applicationInfo = mApplicationInfo;
+        when(mMockPm.getPackageInfo(anyString(), anyInt())).thenReturn(mPackageInfo);
+        when(mMockPm.getPackageInfo(same(LOGGING_PARENT_VALUE), anyInt())).thenThrow(
                 new PackageManager.NameNotFoundException());
         VersionedPackage logPackage = WatchdogRollbackLogger.getLogPackage(mMockContext,
                 sTestPackageV1);
         assertThat(logPackage).isNull();
+        verify(mMockPm, times(1)).getPackageInfo(
+                sTestPackageV1.getPackageName(), PACKAGE_INFO_FLAGS);
+    }
+
+    /**
+     * Ensures that we make the correct Package Manager calls in the case that the failing packages
+     * are correctly configured with parent packages.
+     */
+    @Test
+    public void testApexdLoggingCallsWithParents() throws Exception {
+        for (String failingPackage: sFailingPackages) {
+            PackageInfo packageInfo = new PackageInfo();
+            ApplicationInfo applicationInfo = new ApplicationInfo();
+            Bundle bundle = new Bundle();
+            bundle.putString(LOGGING_PARENT_KEY, getParent(failingPackage));
+            applicationInfo.metaData = bundle;
+            packageInfo.applicationInfo = applicationInfo;
+            when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+        }
+
+        when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+        WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+        for (String failingPackage: sFailingPackages) {
+            verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+            verify(mMockPm, times(1)).getPackageInfo(getParent(failingPackage), 0);
+        }
+    }
+
+    /**
+     * Ensures that we don't make any calls to parent packages in the case that packages are not
+     * correctly configured with parent packages.
+     */
+    @Test
+    public void testApexdLoggingCallsWithNoParents() throws Exception {
+        for (String failingPackage: sFailingPackages) {
+            PackageInfo packageInfo = new PackageInfo();
+            packageInfo.applicationInfo = new ApplicationInfo();
+            when(mMockPm.getPackageInfo(same(failingPackage), anyInt())).thenReturn(packageInfo);
+        }
+        when(mMockPm.getPackageInfo(anyString(), eq(0))).thenReturn(mPackageInfo);
+
+        WatchdogRollbackLogger.logApexdRevert(mMockContext, sFailingPackages, "test_process");
+        verify(mMockPm, times(sFailingPackages.size())).getPackageInfo(anyString(), anyInt());
+        for (String failingPackage: sFailingPackages) {
+            verify(mMockPm, times(1)).getPackageInfo(failingPackage, PACKAGE_INFO_FLAGS);
+        }
+    }
+
+    private String getParent(String packageName) {
+        return packageName + "-parent";
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index fde0ddf..4d0481be 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -144,6 +144,40 @@
         assertEquals("Incorrect blacklist", expectedBlack, actualBlack);
     }
 
+    @Test
+    public void testComponentOverride() throws Exception {
+        final String contents =
+                "<permissions>"
+                + "    <component-override package=\"com.android.package1\">\n"
+                + "        <component class=\"com.android.package1.Full\" enabled=\"true\"/>"
+                + "        <component class=\".Relative\" enabled=\"false\" />\n"
+                + "    </component-override>"
+                + "    <component-override package=\"com.android.package2\" >\n"
+                + "        <component class=\"com.android.package3.Relative2\" enabled=\"yes\" />\n"
+                + "    </component-override>\n"
+                + "</permissions>";
+
+        final File folder = createTempSubfolder("folder");
+        createTempFile(folder, "component-override.xml", contents);
+
+        mSysConfig.readPermissions(folder, /* No permission needed anyway */ 0);
+
+        final ArrayMap<String, Boolean>  packageOneExpected = new ArrayMap<>();
+        packageOneExpected.put("com.android.package1.Full", true);
+        packageOneExpected.put("com.android.package1.Relative", false);
+
+        final ArrayMap<String, Boolean> packageTwoExpected = new ArrayMap<>();
+        packageTwoExpected.put("com.android.package3.Relative2", true);
+
+        final Map<String, Boolean> packageOne = mSysConfig.getComponentsEnabledStates(
+                "com.android.package1");
+        assertEquals(packageOneExpected, packageOne);
+
+        final Map<String, Boolean> packageTwo = mSysConfig.getComponentsEnabledStates(
+                "com.android.package2");
+        assertEquals(packageTwoExpected, packageTwo);
+    }
+
     /**
      * Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
      * @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index 5b5ad87..cbb760a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -44,6 +44,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.internal.matchers.Not;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -239,6 +240,52 @@
         verify(af2, never()).openRead();
     }
 
+    @Test
+    public void testRemoveNotificationRunnable() throws Exception {
+        NotificationHistory nh = mock(NotificationHistory.class);
+        NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
+                mDataBase.new RemoveNotificationRunnable("pkg", 123);
+        rnr.setNotificationHistory(nh);
+
+        AtomicFile af = mock(AtomicFile.class);
+        when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+        mDataBase.mHistoryFiles.addLast(af);
+
+        when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(true);
+
+        mDataBase.mBuffer = mock(NotificationHistory.class);
+
+        rnr.run();
+
+        verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
+        verify(af).openRead();
+        verify(nh).removeNotificationFromWrite("pkg", 123);
+        verify(af).startWrite();
+    }
+
+    @Test
+    public void testRemoveNotificationRunnable_noChanges() throws Exception {
+        NotificationHistory nh = mock(NotificationHistory.class);
+        NotificationHistoryDatabase.RemoveNotificationRunnable rnr =
+                mDataBase.new RemoveNotificationRunnable("pkg", 123);
+        rnr.setNotificationHistory(nh);
+
+        AtomicFile af = mock(AtomicFile.class);
+        when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+        mDataBase.mHistoryFiles.addLast(af);
+
+        when(nh.removeNotificationFromWrite("pkg", 123)).thenReturn(false);
+
+        mDataBase.mBuffer = mock(NotificationHistory.class);
+
+        rnr.run();
+
+        verify(mDataBase.mBuffer).removeNotificationFromWrite("pkg", 123);
+        verify(af).openRead();
+        verify(nh).removeNotificationFromWrite("pkg", 123);
+        verify(af, never()).startWrite();
+    }
+
     private class TestFileAttrProvider implements NotificationHistoryDatabase.FileAttrProvider {
         public Map<File, Long> creationDates = new HashMap<>();
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index b5eb1bf..2c548be 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -289,6 +289,20 @@
     }
 
     @Test
+    public void testDeleteNotificationHistoryItem_userUnlocked() {
+        String pkg = "pkg";
+        long time = 235;
+        NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
+
+        mHistoryManager.onUserUnlocked(USER_SYSTEM);
+        mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
+
+        mHistoryManager.deleteNotificationHistoryItem(pkg, 1, time);
+
+        verify(userHistory, times(1)).deleteNotificationHistoryItem(pkg, time);
+    }
+
+    @Test
     public void testTriggerWriteToDisk() {
         NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
         NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index e0ee3ce..9ec339d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -42,6 +42,8 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -128,6 +130,7 @@
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.notification.Adjustment;
+import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
@@ -6273,4 +6276,60 @@
 
         assertEquals(PRIORITY_CATEGORY_CALLS, actual);
     }
+
+    @Test
+    public void testGetConversationsForPackage_hasShortcut() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+        NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+        channel1.setConversationId("parent1", "convo 1");
+        convo1.setNotificationChannel(channel1);
+        convos.add(convo1);
+
+        ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+        NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+        channel2.setConversationId("parent1", "convo 2");
+        convo2.setNotificationChannel(channel2);
+        convos.add(convo2);
+        when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+        // only one valid shortcut
+        LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery()
+                .setPackage(PKG_P)
+                .setQueryFlags(FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED)
+                .setShortcutIds(Arrays.asList(channel1.getConversationId()));
+        ShortcutInfo si = mock(ShortcutInfo.class);
+        when(si.getShortLabel()).thenReturn("Hello");
+        when(mLauncherApps.getShortcuts(any(), any())).thenReturn(Arrays.asList(si));
+
+        List<ConversationChannelWrapper> conversations =
+                mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+        assertEquals(si, conversations.get(0).getShortcutInfo());
+        assertEquals(si, conversations.get(1).getShortcutInfo());
+
+    }
+
+    @Test
+    public void testGetConversationsForPackage_doesNotHaveShortcut() throws Exception {
+        mService.setPreferencesHelper(mPreferencesHelper);
+        ArrayList<ConversationChannelWrapper> convos = new ArrayList<>();
+        ConversationChannelWrapper convo1 = new ConversationChannelWrapper();
+        NotificationChannel channel1 = new NotificationChannel("a", "a", 1);
+        channel1.setConversationId("parent1", "convo 1");
+        convo1.setNotificationChannel(channel1);
+        convos.add(convo1);
+
+        ConversationChannelWrapper convo2 = new ConversationChannelWrapper();
+        NotificationChannel channel2 = new NotificationChannel("b", "b", 1);
+        channel2.setConversationId("parent1", "convo 2");
+        convo2.setNotificationChannel(channel2);
+        convos.add(convo2);
+        when(mPreferencesHelper.getConversations(anyString(), anyInt())).thenReturn(convos);
+
+        List<ConversationChannelWrapper> conversations =
+                mBinderService.getConversationsForPackage(PKG_P, mUid).getList();
+        assertNull(conversations.get(0).getShortcutInfo());
+        assertNull(conversations.get(1).getShortcutInfo());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index bb84b04..3f690b1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -26,6 +26,8 @@
 
 import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
 
@@ -64,6 +66,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.service.notification.ConversationChannelWrapper;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.TestableContentResolver;
 import android.util.ArrayMap;
@@ -91,6 +94,7 @@
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -2939,4 +2943,97 @@
 
         assertNotNull(mHelper.getNotificationChannel(PKG_O, UID_O, "id", true));
     }
+
+    @Test
+    public void testGetConversations_invalidPkg() {
+        assertThat(mHelper.getConversations("bad", 1)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations_noConversations() {
+        NotificationChannel channel =
+                new NotificationChannel("not_convo", "not_convo", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations_noDisabledGroups() {
+        NotificationChannelGroup group = new NotificationChannelGroup("a", "a");
+        group.setBlocked(true);
+        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+        NotificationChannel parent = new NotificationChannel("parent", "p", 1);
+        mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("convo", "convo", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "convo");
+        channel.setGroup(group.getId());
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations_noDeleted() {
+        NotificationChannel parent = new NotificationChannel("parent", "p", 1);
+        mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+        NotificationChannel channel =
+                new NotificationChannel("convo", "convo", IMPORTANCE_DEFAULT);
+        channel.setConversationId("parent", "convo");
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+        mHelper.deleteNotificationChannel(PKG_O, UID_O, channel.getId());
+
+        assertThat(mHelper.getConversations(PKG_O, UID_O)).isEmpty();
+    }
+
+    @Test
+    public void testGetConversations() {
+        NotificationChannelGroup group = new NotificationChannelGroup("acct", "account_name");
+        mHelper.createNotificationChannelGroup(PKG_O, UID_O, group, true);
+
+        NotificationChannel messages =
+                new NotificationChannel("messages", "Messages", IMPORTANCE_DEFAULT);
+        messages.setGroup(group.getId());
+        mHelper.createNotificationChannel(PKG_O, UID_O, messages, true, false);
+        NotificationChannel calls =
+                new NotificationChannel("calls", "Calls", IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_O, UID_O, calls, true, false);
+
+        NotificationChannel channel =
+                new NotificationChannel("A person", "A lovely person", IMPORTANCE_DEFAULT);
+        channel.setGroup(group.getId());
+        channel.setConversationId(messages.getId(), channel.getName().toString());
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false);
+
+        NotificationChannel channel2 =
+                new NotificationChannel("B person", "B fabulous person", IMPORTANCE_DEFAULT);
+        channel2.setConversationId(calls.getId(), channel.getName().toString());
+        mHelper.createNotificationChannel(PKG_O, UID_O, channel2, true, false);
+
+        Map<String, NotificationChannel> expected = new HashMap<>();
+        expected.put(channel.getId(), channel);
+        expected.put(channel2.getId(), channel2);
+
+        Map<String, CharSequence> expectedGroup = new HashMap<>();
+        expectedGroup.put(channel.getId(), group.getName());
+        expectedGroup.put(channel2.getId(), null);
+
+        Map<String, CharSequence> expectedParentLabel= new HashMap<>();
+        expectedParentLabel.put(channel.getId(), messages.getName());
+        expectedParentLabel.put(channel2.getId(), calls.getName());
+
+        ArrayList<ConversationChannelWrapper> convos = mHelper.getConversations(PKG_O, UID_O);
+        assertThat(convos).hasSize(2);
+
+        for (ConversationChannelWrapper convo : convos) {
+            assertThat(convo.getNotificationChannel())
+                    .isEqualTo(expected.get(convo.getNotificationChannel().getId()));
+            assertThat(convo.getParentChannelLabel())
+                    .isEqualTo(expectedParentLabel.get(convo.getNotificationChannel().getId()));
+            assertThat(convo.getGroupLabel())
+                    .isEqualTo(expectedGroup.get(convo.getNotificationChannel().getId()));
+        }
+    }
 }
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 8c454424..123bb07 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -37,6 +37,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.STATUS_BAR" />
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index a9a20f6..f7aa3cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -26,8 +26,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
@@ -36,14 +38,17 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
 import android.view.Display;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
@@ -207,7 +212,7 @@
         WindowContainerTransaction t = new WindowContainerTransaction();
         Rect newBounds = new Rect(10, 10, 100, 100);
         t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
         assertEquals(newBounds, task.getBounds());
     }
 
@@ -222,7 +227,7 @@
         assertEquals(stack.mRemoteToken, info.stackToken);
         Rect newBounds = new Rect(10, 10, 100, 100);
         t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
         assertEquals(newBounds, stack.getBounds());
     }
 
@@ -235,7 +240,7 @@
         WindowContainerTransaction t = new WindowContainerTransaction();
         assertTrue(task.isFocusable());
         t.setFocusable(stack.mRemoteToken, false);
-        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
         assertFalse(task.isFocusable());
     }
 
@@ -354,6 +359,78 @@
         assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
     }
 
+    @Test
+    public void testHierarchyTransaction() {
+        final ArrayMap<IBinder, RunningTaskInfo> lastReportedTiles = new ArrayMap<>();
+        ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+            @Override
+            public void taskAppeared(RunningTaskInfo taskInfo) { }
+
+            @Override
+            public void taskVanished(IWindowContainer container) { }
+
+            @Override
+            public void transactionReady(int id, SurfaceControl.Transaction t) { }
+
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) {
+                lastReportedTiles.put(info.token.asBinder(), info);
+            }
+        };
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+                listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        final ActivityStack stack = createTaskStackOnDisplay(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        final ActivityStack stack2 = createTaskStackOnDisplay(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+
+        lastReportedTiles.clear();
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
+        wct.reparent(stack2.mRemoteToken, info2.token, true /* onTop */);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
+                null /* organizer */);
+        assertFalse(lastReportedTiles.isEmpty());
+        assertEquals(ACTIVITY_TYPE_STANDARD,
+                lastReportedTiles.get(info1.token.asBinder()).topActivityType);
+        assertEquals(ACTIVITY_TYPE_HOME,
+                lastReportedTiles.get(info2.token.asBinder()).topActivityType);
+
+        lastReportedTiles.clear();
+        wct = new WindowContainerTransaction();
+        wct.reparent(stack2.mRemoteToken, info1.token, false /* onTop */);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
+                null /* organizer */);
+        assertFalse(lastReportedTiles.isEmpty());
+        // Standard should still be on top of tile 1, so no change there
+        assertFalse(lastReportedTiles.containsKey(info1.token.asBinder()));
+        // But tile 2 has no children, so should become undefined
+        assertEquals(ACTIVITY_TYPE_UNDEFINED,
+                lastReportedTiles.get(info2.token.asBinder()).topActivityType);
+
+        // Check the getChildren call
+        List<RunningTaskInfo> children =
+                mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token);
+        assertEquals(2, children.size());
+        children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token);
+        assertEquals(0, children.size());
+
+        lastReportedTiles.clear();
+        wct = new WindowContainerTransaction();
+        wct.reorder(stack2.mRemoteToken, true /* onTop */);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(wct,
+                null /* organizer */);
+        // Home should now be on top. No change occurs in second tile, so not reported
+        assertEquals(1, lastReportedTiles.size());
+        assertEquals(ACTIVITY_TYPE_HOME,
+                lastReportedTiles.get(info1.token.asBinder()).topActivityType);
+    }
+
     private List<TaskTile> getTaskTiles(DisplayContent dc) {
         ArrayList<TaskTile> out = new ArrayList<>();
         for (int i = dc.getStackCount() - 1; i >= 0; --i) {
@@ -364,4 +441,46 @@
         }
         return out;
     }
+
+    @Test
+    public void testTrivialBLASTCallback() throws RemoteException {
+        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+
+        BLASTSyncEngine bse = new BLASTSyncEngine();
+
+        BLASTSyncEngine.TransactionReadyListener transactionListener =
+            mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(transactionListener);
+        bse.addToSyncSet(id, task);
+        bse.setReady(id);
+        // Since this task has no windows the sync is trivial and completes immediately.
+        verify(transactionListener)
+            .transactionReady(anyInt(), any());
+    }
+
+    @Test
+    public void testBLASTCallbackWithWindow() {
+        final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(stackController1, 0 /* userId */);
+        final ITaskOrganizer organizer = registerMockOrganizer();
+        final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window");
+
+        BLASTSyncEngine bse = new BLASTSyncEngine();
+
+        BLASTSyncEngine.TransactionReadyListener transactionListener =
+            mock(BLASTSyncEngine.TransactionReadyListener.class);
+
+        int id = bse.startSyncSet(transactionListener);
+        bse.addToSyncSet(id, task);
+        bse.setReady(id);
+        // Since we have a window we have to wait for it to draw to finish sync.
+        verify(transactionListener, never())
+            .transactionReady(anyInt(), any());
+        w.finishDrawing(null);
+        verify(transactionListener)
+            .transactionReady(anyInt(), any());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 7aaf3fb..52b465f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -20,7 +20,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
+import static com.android.internal.policy.TaskResizingAlgorithm.MIN_ASPECT;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP;
 import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
new file mode 100644
index 0000000..35723ab
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class WindowManagerServiceTests extends WindowTestsBase {
+    @Rule
+    public ExpectedException mExpectedException = ExpectedException.none();
+
+    @Test
+    public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() {
+        if (!isAutomotive()) {
+            mExpectedException.expect(UnsupportedOperationException.class);
+
+            mWm.setForceShowSystemBars(true);
+        }
+    }
+
+    @Test
+    public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() {
+        if (isAutomotive()) {
+            mExpectedException.none();
+
+            mWm.setForceShowSystemBars(true);
+        }
+    }
+
+    private boolean isAutomotive() {
+        return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE);
+    }
+}
diff --git a/telephony/common/com/google/android/mms/pdu/CharacterSets.java b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
index 5172b7b..a3894d6 100644
--- a/telephony/common/com/google/android/mms/pdu/CharacterSets.java
+++ b/telephony/common/com/google/android/mms/pdu/CharacterSets.java
@@ -48,6 +48,51 @@
     public static final int UTF_16      = 0x03F7;
 
     /**
+     * Extend charsets.
+     *
+     * From http://www.iana.org/assignments/character-sets/
+     */
+    public static final int BIG5_HKSCS = 0x0835; //2101
+    public static final int BOCU_1 = 0x03FC; //1020
+    public static final int CESU_8 = 0x03F8; //1016
+    public static final int CP864 = 0x0803; //2051
+    public static final int EUC_JP = 0x12; //18
+    public static final int EUC_KR = 0x26; //38
+    public static final int GB18030 = 0x72; //114
+    public static final int GBK = 0x71; //113
+    public static final int HZ_GB_2312 = 0x0825; //2085
+    public static final int GB_2312 = 0x07E9; //2025
+    public static final int ISO_2022_CN = 0x68; //104
+    public static final int ISO_2022_CN_EXT = 0x69; //105
+    public static final int ISO_2022_JP = 0x27; //39
+    public static final int ISO_2022_KR = 0x25; //37
+    public static final int ISO_8859_10 = 0x0D; //13
+    public static final int ISO_8859_13 = 0x6D; //109
+    public static final int ISO_8859_14 = 0x6E; //110
+    public static final int ISO_8859_15 = 0x6F; //111
+    public static final int ISO_8859_16 = 0x70; //112
+    public static final int KOI8_R = 0x0824; //2084
+    public static final int KOI8_U = 0x0828; //2088
+    public static final int MACINTOSH = 0x07EB; //2027
+    public static final int SCSU = 0x03F3; //1011
+    public static final int TIS_620 = 0x08D3; //2259
+    public static final int UTF_16BE = 0x03F5; //1013
+    public static final int UTF_16LE = 0x03F6; //1014
+    public static final int UTF_32 = 0x03F9; //1017
+    public static final int UTF_32BE = 0x03FA; //1018
+    public static final int UTF_32LE = 0x03FB; //1019
+    public static final int UTF_7 = 0x03F4; //1012
+    public static final int WINDOWS_1250 = 0x08CA; //2250
+    public static final int WINDOWS_1251 = 0x08CB; //2251
+    public static final int WINDOWS_1252 = 0x08CC; //2252
+    public static final int WINDOWS_1253 = 0x08CD; //2253
+    public static final int WINDOWS_1254 = 0x08CE; //2254
+    public static final int WINDOWS_1255 = 0x08CF; //2255
+    public static final int WINDOWS_1256 = 0x08D0; //2256
+    public static final int WINDOWS_1257 = 0x08D1; //2257
+    public static final int WINDOWS_1258 = 0x08D2; //2258
+
+    /**
      * If the encoding of given data is unsupported, use UTF_8 to decode it.
      */
     public static final int DEFAULT_CHARSET = UTF_8;
@@ -72,6 +117,45 @@
         BIG5,
         UCS2,
         UTF_16,
+        BIG5_HKSCS,
+        BOCU_1,
+        CESU_8,
+        CP864,
+        EUC_JP,
+        EUC_KR,
+        GB18030,
+        GBK,
+        HZ_GB_2312,
+        GB_2312,
+        ISO_2022_CN,
+        ISO_2022_CN_EXT,
+        ISO_2022_JP,
+        ISO_2022_KR,
+        ISO_8859_10,
+        ISO_8859_13,
+        ISO_8859_14,
+        ISO_8859_15,
+        ISO_8859_16,
+        KOI8_R,
+        KOI8_U,
+        MACINTOSH,
+        SCSU,
+        TIS_620,
+        UTF_16BE,
+        UTF_16LE,
+        UTF_32,
+        UTF_32BE,
+        UTF_32LE,
+        UTF_7,
+        WINDOWS_1250,
+        WINDOWS_1251,
+        WINDOWS_1252,
+        WINDOWS_1253,
+        WINDOWS_1254,
+        WINDOWS_1255,
+        WINDOWS_1256,
+        WINDOWS_1257,
+        WINDOWS_1258,
     };
 
     /**
@@ -94,6 +178,51 @@
     public static final String MIMENAME_UCS2        = "iso-10646-ucs-2";
     public static final String MIMENAME_UTF_16      = "utf-16";
 
+    /**
+     * Extend charsets.
+     *
+     * From http://www.iana.org/assignments/character-sets/
+     */
+    public static final String MIMENAME_BIG5_HKSCS = "Big5-HKSCS";
+    public static final String MIMENAME_BOCU_1 = "BOCU-1";
+    public static final String MIMENAME_CESU_8 = "CESU-8";
+    public static final String MIMENAME_CP864 = "cp864";
+    public static final String MIMENAME_EUC_JP = "EUC-JP";
+    public static final String MIMENAME_EUC_KR = "EUC-KR";
+    public static final String MIMENAME_GB18030 = "GB18030";
+    public static final String MIMENAME_GBK = "GBK";
+    public static final String MIMENAME_HZ_GB_2312 = "HZ-GB-2312";
+    public static final String MIMENAME_GB_2312 = "GB2312";
+    public static final String MIMENAME_ISO_2022_CN = "ISO-2022-CN";
+    public static final String MIMENAME_ISO_2022_CN_EXT = "ISO-2022-CN-EXT";
+    public static final String MIMENAME_ISO_2022_JP = "ISO-2022-JP";
+    public static final String MIMENAME_ISO_2022_KR = "ISO-2022-KR";
+    public static final String MIMENAME_ISO_8859_10 = "ISO-8859-10";
+    public static final String MIMENAME_ISO_8859_13 = "ISO-8859-13";
+    public static final String MIMENAME_ISO_8859_14 = "ISO-8859-14";
+    public static final String MIMENAME_ISO_8859_15 = "ISO-8859-15";
+    public static final String MIMENAME_ISO_8859_16 = "ISO-8859-16";
+    public static final String MIMENAME_KOI8_R = "KOI8-R";
+    public static final String MIMENAME_KOI8_U = "KOI8-U";
+    public static final String MIMENAME_MACINTOSH = "macintosh";
+    public static final String MIMENAME_SCSU = "SCSU";
+    public static final String MIMENAME_TIS_620 = "TIS-620";
+    public static final String MIMENAME_UTF_16BE = "UTF-16BE";
+    public static final String MIMENAME_UTF_16LE = "UTF-16LE";
+    public static final String MIMENAME_UTF_32 = "UTF-32";
+    public static final String MIMENAME_UTF_32BE = "UTF-32BE";
+    public static final String MIMENAME_UTF_32LE = "UTF-32LE";
+    public static final String MIMENAME_UTF_7 = "UTF-7";
+    public static final String MIMENAME_WINDOWS_1250 = "windows-1250";
+    public static final String MIMENAME_WINDOWS_1251 = "windows-1251";
+    public static final String MIMENAME_WINDOWS_1252 = "windows-1252";
+    public static final String MIMENAME_WINDOWS_1253 = "windows-1253";
+    public static final String MIMENAME_WINDOWS_1254 = "windows-1254";
+    public static final String MIMENAME_WINDOWS_1255 = "windows-1255";
+    public static final String MIMENAME_WINDOWS_1256 = "windows-1256";
+    public static final String MIMENAME_WINDOWS_1257 = "windows-1257";
+    public static final String MIMENAME_WINDOWS_1258 = "windows-1258";
+
     public static final String DEFAULT_CHARSET_NAME = MIMENAME_UTF_8;
 
     /**
@@ -116,6 +245,45 @@
         MIMENAME_BIG5,
         MIMENAME_UCS2,
         MIMENAME_UTF_16,
+        MIMENAME_BIG5_HKSCS,
+        MIMENAME_BOCU_1,
+        MIMENAME_CESU_8,
+        MIMENAME_CP864,
+        MIMENAME_EUC_JP,
+        MIMENAME_EUC_KR,
+        MIMENAME_GB18030,
+        MIMENAME_GBK,
+        MIMENAME_HZ_GB_2312,
+        MIMENAME_GB_2312,
+        MIMENAME_ISO_2022_CN,
+        MIMENAME_ISO_2022_CN_EXT,
+        MIMENAME_ISO_2022_JP,
+        MIMENAME_ISO_2022_KR,
+        MIMENAME_ISO_8859_10,
+        MIMENAME_ISO_8859_13,
+        MIMENAME_ISO_8859_14,
+        MIMENAME_ISO_8859_15,
+        MIMENAME_ISO_8859_16,
+        MIMENAME_KOI8_R,
+        MIMENAME_KOI8_U,
+        MIMENAME_MACINTOSH,
+        MIMENAME_SCSU,
+        MIMENAME_TIS_620,
+        MIMENAME_UTF_16BE,
+        MIMENAME_UTF_16LE,
+        MIMENAME_UTF_32,
+        MIMENAME_UTF_32BE,
+        MIMENAME_UTF_32LE,
+        MIMENAME_UTF_7,
+        MIMENAME_WINDOWS_1250,
+        MIMENAME_WINDOWS_1251,
+        MIMENAME_WINDOWS_1252,
+        MIMENAME_WINDOWS_1253,
+        MIMENAME_WINDOWS_1254,
+        MIMENAME_WINDOWS_1255,
+        MIMENAME_WINDOWS_1256,
+        MIMENAME_WINDOWS_1257,
+        MIMENAME_WINDOWS_1258,
     };
 
     private static final HashMap<Integer, String> MIBENUM_TO_NAME_MAP;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e3d031d..e520a02 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7761,9 +7761,8 @@
      *
      * @hide
      */
-    @SystemApi
     public static final int DEFAULT_PREFERRED_NETWORK_MODE =
-            RILConstants.DEFAULT_PREFERRED_NETWORK_MODE;
+            RILConstants.PREFERRED_NETWORK_MODE;
 
     /**
      * Get the preferred network type.
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index e16fffa..e1aec0a 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.service.euicc.EuiccProfileInfo;
 import android.telephony.TelephonyFrameworkInitializer;
@@ -168,7 +169,12 @@
                     new IGetAllProfilesCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo[] profiles) {
-                            executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profiles));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -192,7 +198,12 @@
                     new IGetProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            executor.execute(() -> callback.onComplete(resultCode, profile));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -217,7 +228,12 @@
                     refresh, new IDisableProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -243,7 +259,12 @@
                     refresh, new ISwitchToProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccProfileInfo profile) {
-                            executor.execute(() -> callback.onComplete(resultCode, profile));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, profile));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -268,7 +289,12 @@
                     nickname, new ISetNicknameCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -292,7 +318,12 @@
                     new IDeleteProfileCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -317,7 +348,12 @@
                     new IResetMemoryCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -340,7 +376,12 @@
                     new IGetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            executor.execute(() -> callback.onComplete(resultCode, address));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -363,7 +404,12 @@
                     new IGetSmdsAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, String address) {
-                            executor.execute(() -> callback.onComplete(resultCode, address));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, address));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -388,7 +434,12 @@
                     new ISetDefaultSmdpAddressCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -411,7 +462,12 @@
                     new IGetRulesAuthTableCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccRulesAuthTable rat) {
-                            executor.execute(() -> callback.onComplete(resultCode, rat));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, rat));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -434,7 +490,12 @@
                     new IGetEuiccChallengeCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] challenge) {
-                            executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, challenge));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -457,7 +518,12 @@
                     new IGetEuiccInfo1Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            executor.execute(() -> callback.onComplete(resultCode, info));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -480,7 +546,12 @@
                     new IGetEuiccInfo2Callback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] info) {
-                            executor.execute(() -> callback.onComplete(resultCode, info));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, info));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -522,7 +593,12 @@
                     new IAuthenticateServerCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -561,7 +637,12 @@
                     new IPrepareDownloadCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -589,7 +670,12 @@
                     new ILoadBoundProfilePackageCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -619,7 +705,12 @@
                     new ICancelSessionCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, byte[] response) {
-                            executor.execute(() -> callback.onComplete(resultCode, response));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, response));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -643,7 +734,13 @@
                     new IListNotificationsCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            executor.execute(() -> callback.onComplete(resultCode, notifications));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -667,7 +764,13 @@
                     events, new IRetrieveNotificationListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification[] notifications) {
-                            executor.execute(() -> callback.onComplete(resultCode, notifications));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notifications));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -691,7 +794,13 @@
                     seqNumber, new IRetrieveNotificationCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode, EuiccNotification notification) {
-                            executor.execute(() -> callback.onComplete(resultCode, notification));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(
+                                        resultCode, notification));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
@@ -718,7 +827,12 @@
                     new IRemoveNotificationFromListCallback.Stub() {
                         @Override
                         public void onComplete(int resultCode) {
-                            executor.execute(() -> callback.onComplete(resultCode, null));
+                            final long token = Binder.clearCallingIdentity();
+                            try {
+                                executor.execute(() -> callback.onComplete(resultCode, null));
+                            } finally {
+                                Binder.restoreCallingIdentity(token);
+                            }
                         }
                     });
         } catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9ac8cb1..c40573b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -233,14 +233,11 @@
     /** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
     int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
 
-    /** Default preferred network mode */
-    int DEFAULT_PREFERRED_NETWORK_MODE = NETWORK_MODE_WCDMA_PREF;
-
     @UnsupportedAppUsage
     int PREFERRED_NETWORK_MODE = Optional.of(TelephonyProperties.default_network())
             .filter(list -> !list.isEmpty())
             .map(list -> list.get(0))
-            .orElse(DEFAULT_PREFERRED_NETWORK_MODE);
+            .orElse(NETWORK_MODE_WCDMA_PREF);
 
     int BAND_MODE_UNSPECIFIED = 0;      //"unspecified" (selected by baseband automatically)
     int BAND_MODE_EURO = 1;             //"EURO band" (GSM-900 / DCS-1800 / WCDMA-IMT-2000)
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 98e7b4e..89005da 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -36,6 +36,15 @@
 }
 
 java_test_host {
+    name: "NetworkStagedRollbackTest",
+    srcs: ["NetworkStagedRollbackTest/src/**/*.java"],
+    libs: ["tradefed"],
+    static_libs: ["testng"],
+    test_suites: ["general-tests"],
+    test_config: "NetworkStagedRollbackTest.xml",
+}
+
+java_test_host {
     name: "MultiUserRollbackTest",
     srcs: ["MultiUserRollbackTest/src/**/*.java"],
     libs: ["tradefed"],
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest.xml b/tests/RollbackTest/NetworkStagedRollbackTest.xml
new file mode 100644
index 0000000..a465a4f
--- /dev/null
+++ b/tests/RollbackTest/NetworkStagedRollbackTest.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.
+-->
+<configuration description="Runs the network staged rollback tests">
+    <option name="test-suite-tag" value="NetworkStagedRollbackTest" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="RollbackTest.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="class" value="com.android.tests.rollback.host.NetworkStagedRollbackTest" />
+    </test>
+</configuration>
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
new file mode 100644
index 0000000..2c2e828
--- /dev/null
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.host;
+
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Runs the network rollback tests.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class NetworkStagedRollbackTest extends BaseHostJUnit4Test {
+    /**
+     * Tests failed network health check triggers watchdog staged rollbacks.
+     */
+    @Test
+    public void testNetworkFailedRollback() throws Exception {
+    }
+
+    /**
+     * Tests passed network health check does not trigger watchdog staged rollbacks.
+     */
+    @Test
+    public void testNetworkPassedDoesNotRollback() throws Exception {
+    }
+}
diff --git a/tests/RollbackTest/RollbackTest.xml b/tests/RollbackTest/RollbackTest.xml
index a14b01c..f2c0f86 100644
--- a/tests/RollbackTest/RollbackTest.xml
+++ b/tests/RollbackTest/RollbackTest.xml
@@ -22,9 +22,9 @@
         <option name="package" value="com.android.tests.rollback" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
 
-        <!-- Exclude the StagedRollbackTest and MultiUserRollbackTest tests, which need to be
-             specially driven from the StagedRollbackTest and MultiUserRollbackTest host test -->
+        <!-- Exclude the device tests which need to be specially driven from the host tests -->
         <option name="exclude-filter" value="com.android.tests.rollback.StagedRollbackTest" />
+        <option name="exclude-filter" value="com.android.tests.rollback.NetworkStagedRollbackTest" />
         <option name="exclude-filter" value="com.android.tests.rollback.MultiUserRollbackTest" />
     </test>
 </configuration>
diff --git a/core/java/android/os/StatsDimensionsValue.aidl b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
similarity index 64%
copy from core/java/android/os/StatsDimensionsValue.aidl
copy to tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
index 81a14a4..04004d6 100644
--- a/core/java/android/os/StatsDimensionsValue.aidl
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2018, 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.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      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,
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-package android.os;
+package com.android.tests.rollback;
 
-/** @hide */
-parcelable StatsDimensionsValue cpp_header "android/os/StatsDimensionsValue.h";
\ No newline at end of file
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NetworkStagedRollbackTest {
+}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index 8f3cb34..bdfaaa8 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -45,7 +45,7 @@
             final WindowContainerTransaction wct = new WindowContainerTransaction();
             wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
             try {
-                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct);
+                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null);
             } catch (Exception e) {
             }
         }
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 3e4f3d8..efea91a 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -272,10 +272,24 @@
         netCap.setOwnerUid(123);
         assertParcelingIsLossless(netCap);
         netCap.setSSID(TEST_SSID);
-        assertParcelSane(netCap, 13);
+        assertParcelSane(netCap, 15);
     }
 
     @Test
+    public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
+        final NetworkCapabilities netCap = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .setRequestorUid(9304)
+                .setRequestorPackageName("com.android.test")
+                .addCapability(NET_CAPABILITY_EIMS)
+                .addCapability(NET_CAPABILITY_NOT_METERED);
+        assertParcelingIsLossless(netCap);
+        netCap.setSSID(TEST_SSID);
+        assertParcelSane(netCap, 15);
+    }
+
+
+    @Test
     public void testOemPaid() {
         NetworkCapabilities nc = new NetworkCapabilities();
         // By default OEM_PAID is neither in the unwanted or required lists and the network is not
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 7ede144..d6bf334 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -212,7 +212,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+        when(mService.requestNetwork(
+                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
                 .thenReturn(request);
         manager.requestNetwork(request, callback, handler);
 
@@ -240,7 +241,8 @@
         ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
 
         // register callback
-        when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+        when(mService.requestNetwork(
+                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
                 .thenReturn(req1);
         manager.requestNetwork(req1, callback, handler);
 
@@ -258,7 +260,8 @@
         verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
 
         // callback can be registered again
-        when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+        when(mService.requestNetwork(
+                any(), captor.capture(), anyInt(), any(), anyInt(), any()))
                 .thenReturn(req2);
         manager.requestNetwork(req2, callback, handler);
 
@@ -282,7 +285,8 @@
         info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
 
         when(mCtx.getApplicationInfo()).thenReturn(info);
-        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request);
+        when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any()))
+                .thenReturn(request);
 
         Handler handler = new Handler(Looper.getMainLooper());
         manager.requestNetwork(request, callback, handler);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a0e98d0..968f552 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -107,6 +107,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -305,6 +306,7 @@
     private static final String MOBILE_IFNAME = "test_rmnet_data0";
     private static final String WIFI_IFNAME = "test_wlan0";
     private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+    private static final String TEST_PACKAGE_NAME = "com.android.test.package";
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     private MockContext mServiceContext;
@@ -654,7 +656,7 @@
 
             if (mNmValidationRedirectUrl != null) {
                 mNmCallbacks.showProvisioningNotification(
-                        "test_provisioning_notif_action", "com.android.test.package");
+                        "test_provisioning_notif_action", TEST_PACKAGE_NAME);
                 mNmProvNotificationRequested = true;
             }
         }
@@ -2972,7 +2974,7 @@
             networkCapabilities.addTransportType(TRANSPORT_WIFI)
                     .setNetworkSpecifier(new MatchAllNetworkSpecifier());
             mService.requestNetwork(networkCapabilities, null, 0, null,
-                    ConnectivityManager.TYPE_WIFI);
+                    ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
         });
 
         class NonParcelableSpecifier extends NetworkSpecifier {
@@ -3011,31 +3013,12 @@
     }
 
     @Test
-    public void testNetworkSpecifierUidSpoofSecurityException() throws Exception {
-        class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
-            @Override
-            public boolean satisfiedBy(NetworkSpecifier other) {
-                return true;
-            }
-
-            @Override
-            public void assertValidFromUid(int requestorUid) {
-                throw new SecurityException("failure");
-            }
-
-            @Override
-            public int describeContents() { return 0; }
-            @Override
-            public void writeToParcel(Parcel dest, int flags) {}
-        }
-
+    public void testNetworkRequestUidSpoofSecurityException() throws Exception {
         mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
         mWiFiNetworkAgent.connect(false);
-
-        UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
-        NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier(
-                networkSpecifier).build();
+        NetworkRequest networkRequest = newWifiRequestBuilder().build();
         TestNetworkCallback networkCallback = new TestNetworkCallback();
+        doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
         assertThrows(SecurityException.class, () -> {
             mCm.requestNetwork(networkRequest, networkCallback);
         });
diff --git a/wifi/Android.bp b/wifi/Android.bp
index 6a29b1c..1763975 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -47,7 +47,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/WifiOemMigrationHook.java",
         "java/android/net/wifi/wificond/*.java",
         ":libwificond_ipc_aidl",
     ],
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1f1c0c1..221f644 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -266,4 +266,8 @@
      * Return the Map of {@link WifiNetworkSuggestion} and the list of <ScanResult>
      */
     Map getMatchingScanResults(in List<WifiNetworkSuggestion> networkSuggestions, in List<ScanResult> scanResults, String callingPackage, String callingFeatureId);
+
+    void setScanThrottleEnabled(boolean enable);
+
+    boolean isScanThrottleEnabled();
 }
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index c8fd243..0db3313 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -519,6 +519,9 @@
             case BAND_5GHZ:
                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_5GHZ;
                 break;
+            case BAND_2GHZ | BAND_5GHZ:
+                wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
+                break;
             case BAND_ANY:
                 wifiConfig.apBand  = WifiConfiguration.AP_BAND_ANY;
                 break;
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 7c031ea..24b2a8e 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -449,9 +449,8 @@
      * <p>
      * If the SSID can be decoded as UTF-8, it will be returned surrounded by double
      * quotation marks. Otherwise, it is returned as a string of hex digits.
-     * The SSID may be
-     * <lt>&lt;unknown ssid&gt;, if there is no network currently connected or if the caller has
-     * insufficient permissions to access the SSID.<lt>
+     * The SSID may be {@link WifiManager#UNKNOWN_SSID}, if there is no network currently connected
+     * or if the caller has insufficient permissions to access the SSID.
      * </p>
      * <p>
      * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 1dc4a06..5ccc3aa 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2625,8 +2625,8 @@
     public void getWifiActivityEnergyInfoAsync(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnWifiActivityEnergyInfoListener listener) {
-        if (executor == null) throw new IllegalArgumentException("executor cannot be null");
-        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         try {
             mService.getWifiActivityEnergyInfoAsync(
                     new OnWifiActivityEnergyInfoProxy(executor, listener));
@@ -2982,7 +2982,7 @@
     }
 
     /**
-     * Start Soft AP (hotspot) mode with the specified configuration.
+     * Start Soft AP (hotspot) mode for tethering purposes with the specified configuration.
      * Note that starting Soft AP mode may disable station mode operation if the device does not
      * support concurrency.
      * @param wifiConfig SSID, security and channel details as part of WifiConfiguration, or null to
@@ -3278,7 +3278,7 @@
     }
 
     /**
-     * Gets the Wi-Fi enabled state.
+     * Gets the tethered Wi-Fi hotspot enabled state.
      * @return One of {@link #WIFI_AP_STATE_DISABLED},
      *         {@link #WIFI_AP_STATE_DISABLING}, {@link #WIFI_AP_STATE_ENABLED},
      *         {@link #WIFI_AP_STATE_ENABLING}, {@link #WIFI_AP_STATE_FAILED}
@@ -3297,8 +3297,8 @@
     }
 
     /**
-     * Return whether Wi-Fi AP is enabled or disabled.
-     * @return {@code true} if Wi-Fi AP is enabled
+     * Return whether tethered Wi-Fi AP is enabled or disabled.
+     * @return {@code true} if tethered  Wi-Fi AP is enabled
      * @see #getWifiApState()
      *
      * @hide
@@ -3310,7 +3310,7 @@
     }
 
     /**
-     * Gets the Wi-Fi AP Configuration.
+     * Gets the tethered Wi-Fi AP Configuration.
      * @return AP details in WifiConfiguration
      *
      * Note that AP detail may contain configuration which is cannot be represented
@@ -3332,7 +3332,7 @@
     }
 
     /**
-     * Gets the Wi-Fi AP Configuration.
+     * Gets the Wi-Fi tethered AP Configuration.
      * @return AP details in {@link SoftApConfiguration}
      *
      * @hide
@@ -3349,7 +3349,7 @@
     }
 
     /**
-     * Sets the Wi-Fi AP Configuration.
+     * Sets the tethered Wi-Fi AP Configuration.
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
      * @deprecated This API is deprecated. Use {@link #setSoftApConfiguration(SoftApConfiguration)}
@@ -3368,9 +3368,9 @@
     }
 
     /**
-     * Sets the Wi-Fi AP Configuration.
+     * Sets the tethered Wi-Fi AP Configuration.
      *
-     * If the API is called while the soft AP is enabled, the configuration will apply to
+     * If the API is called while the tethered 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)}
@@ -6118,4 +6118,48 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Enable/disable wifi scan throttling from 3rd party apps.
+     *
+     * <p>
+     * The throttling limits for apps are described in
+     * <a href="Wi-Fi Scan Throttling">
+     * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling</a>
+     * </p>
+     *
+     * @param enable true to allow scan throttling, false to disallow scan throttling.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void setScanThrottleEnabled(boolean enable) {
+        try {
+            mService.setScanThrottleEnabled(enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the persisted Wi-Fi scan throttle state. Defaults to true, unless changed by the user via
+     * Developer options.
+     *
+     * <p>
+     * The throttling limits for apps are described in
+     * <a href="Wi-Fi Scan Throttling">
+     * https://developer.android.com/guide/topics/connectivity/wifi-scan#wifi-scan-throttling</a>
+     * </p>
+     *
+     * @return true to indicate that scan throttling is enabled, false to indicate that scan
+     * throttling is disabled.
+     */
+    @RequiresPermission(ACCESS_WIFI_STATE)
+    public boolean isScanThrottleEnabled() {
+        try {
+            return mService.isScanThrottleEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index 04d2e1a..6632c16 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -27,7 +27,6 @@
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
 
 import java.util.Objects;
 
@@ -41,33 +40,10 @@
      */
     private final WifiConfiguration mWifiConfiguration;
 
-    /**
-     * The UID of the app that requested a specific wifi network using {@link WifiNetworkSpecifier}.
-     *
-     * Will only be filled when the device connects to a wifi network as a result of a
-     * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to -1 if the device
-     * auto-connected to a wifi network.
-     */
-    private final int mOriginalRequestorUid;
-
-    /**
-     * The package name of the app that requested a specific wifi network using
-     * {@link WifiNetworkSpecifier}.
-     *
-     * Will only be filled when the device connects to a wifi network as a result of a
-     * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to null if the device
-     * auto-connected to a wifi network.
-     */
-    private final String mOriginalRequestorPackageName;
-
-    public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration,
-                                     int originalRequestorUid,
-                                     @Nullable String originalRequestorPackageName) {
+    public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration) {
         checkNotNull(wifiConfiguration);
 
         mWifiConfiguration = wifiConfiguration;
-        mOriginalRequestorUid = originalRequestorUid;
-        mOriginalRequestorPackageName = originalRequestorPackageName;
     }
 
     /**
@@ -78,10 +54,7 @@
                 @Override
                 public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) {
                     WifiConfiguration wifiConfiguration = in.readParcelable(null);
-                    int originalRequestorUid = in.readInt();
-                    String originalRequestorPackageName = in.readString();
-                    return new WifiNetworkAgentSpecifier(
-                            wifiConfiguration, originalRequestorUid, originalRequestorPackageName);
+                    return new WifiNetworkAgentSpecifier(wifiConfiguration);
                 }
 
                 @Override
@@ -98,8 +71,6 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelable(mWifiConfiguration, flags);
-        dest.writeInt(mOriginalRequestorUid);
-        dest.writeString(mOriginalRequestorPackageName);
     }
 
     @Override
@@ -149,12 +120,6 @@
                 this.mWifiConfiguration.allowedKeyManagement)) {
             return false;
         }
-        if (ns.requestorUid != this.mOriginalRequestorUid) {
-            return false;
-        }
-        if (!TextUtils.equals(ns.requestorPackageName, this.mOriginalRequestorPackageName)) {
-            return false;
-        }
         return true;
     }
 
@@ -163,9 +128,7 @@
         return Objects.hash(
                 mWifiConfiguration.SSID,
                 mWifiConfiguration.BSSID,
-                mWifiConfiguration.allowedKeyManagement,
-                mOriginalRequestorUid,
-                mOriginalRequestorPackageName);
+                mWifiConfiguration.allowedKeyManagement);
     }
 
     @Override
@@ -180,10 +143,7 @@
         return Objects.equals(this.mWifiConfiguration.SSID, lhs.mWifiConfiguration.SSID)
                 && Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID)
                 && Objects.equals(this.mWifiConfiguration.allowedKeyManagement,
-                    lhs.mWifiConfiguration.allowedKeyManagement)
-                && mOriginalRequestorUid == lhs.mOriginalRequestorUid
-                && TextUtils.equals(mOriginalRequestorPackageName,
-                lhs.mOriginalRequestorPackageName);
+                    lhs.mWifiConfiguration.allowedKeyManagement);
     }
 
     @Override
@@ -192,19 +152,11 @@
         sb.append("WifiConfiguration=")
                 .append(", SSID=").append(mWifiConfiguration.SSID)
                 .append(", BSSID=").append(mWifiConfiguration.BSSID)
-                .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid)
-                .append(", mOriginalRequestorPackageName=").append(mOriginalRequestorPackageName)
                 .append("]");
         return sb.toString();
     }
 
     @Override
-    public void assertValidFromUid(int requestorUid) {
-        throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used "
-                + "for requests.");
-    }
-
-    @Override
     public NetworkSpecifier redact() {
         return null;
     }
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 444e1ef..3d946c9 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.Application;
 import android.net.MacAddress;
 import android.net.MatchAllNetworkSpecifier;
 import android.net.NetworkRequest;
@@ -28,13 +27,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PatternMatcher;
-import android.os.Process;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.Pair;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.Objects;
@@ -438,24 +433,7 @@
             return new WifiNetworkSpecifier(
                     mSsidPatternMatcher,
                     mBssidPatternMatcher,
-                    buildWifiConfiguration(),
-                    Process.myUid(),
-                    getCurrentApplicationReflectively().getApplicationContext().getOpPackageName());
-        }
-
-        // TODO(b/144102365): Remove once refactor is complete
-        private static Application getCurrentApplicationReflectively() {
-            try {
-                // reflection for static method android.app.ActivityThread#currentApplication()
-                Class<?> klass = Class.forName("android.app.ActivityThread");
-                Method currentApplicationMethod = klass.getDeclaredMethod("currentApplication");
-                Object result = currentApplicationMethod.invoke(null);
-                return (Application) result;
-            } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException
-                    | InvocationTargetException e) {
-                Log.e(TAG, "Failed to call ActivityThread#currentApplication() reflectively!", e);
-                throw new RuntimeException(e);
-            }
+                    buildWifiConfiguration());
         }
     }
 
@@ -483,20 +461,6 @@
      */
     public final WifiConfiguration wifiConfiguration;
 
-    /**
-     * The UID of the process initializing this network specifier. Validated by receiver using
-     * checkUidIfNecessary() and is used by satisfiedBy() to determine whether the specifier
-     * matches the offered network.
-     * @hide
-     */
-    public final int requestorUid;
-
-    /**
-     * The package name of the app initializing this network specifier.
-     * @hide
-     */
-    public final String requestorPackageName;
-
     /** @hide */
     public WifiNetworkSpecifier() throws IllegalAccessException {
         throw new IllegalAccessException("Use the builder to create an instance");
@@ -505,18 +469,14 @@
     /** @hide */
     public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher,
                                 @NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher,
-                                @NonNull WifiConfiguration wifiConfiguration,
-                                int requestorUid, @NonNull String requestorPackageName) {
+                                @NonNull WifiConfiguration wifiConfiguration) {
         checkNotNull(ssidPatternMatcher);
         checkNotNull(bssidPatternMatcher);
         checkNotNull(wifiConfiguration);
-        checkNotNull(requestorPackageName);
 
         this.ssidPatternMatcher = ssidPatternMatcher;
         this.bssidPatternMatcher = bssidPatternMatcher;
         this.wifiConfiguration = wifiConfiguration;
-        this.requestorUid = requestorUid;
-        this.requestorPackageName = requestorPackageName;
     }
 
     public static final @NonNull Creator<WifiNetworkSpecifier> CREATOR =
@@ -529,10 +489,8 @@
                     Pair<MacAddress, MacAddress> bssidPatternMatcher =
                             Pair.create(baseAddress, mask);
                     WifiConfiguration wifiConfiguration = in.readParcelable(null);
-                    int requestorUid = in.readInt();
-                    String requestorPackageName = in.readString();
                     return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher,
-                            wifiConfiguration, requestorUid, requestorPackageName);
+                            wifiConfiguration);
                 }
 
                 @Override
@@ -552,18 +510,13 @@
         dest.writeParcelable(bssidPatternMatcher.first, flags);
         dest.writeParcelable(bssidPatternMatcher.second, flags);
         dest.writeParcelable(wifiConfiguration, flags);
-        dest.writeInt(requestorUid);
-        dest.writeString(requestorPackageName);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(
-                ssidPatternMatcher.getPath(),
-                ssidPatternMatcher.getType(),
-                bssidPatternMatcher,
-                wifiConfiguration.allowedKeyManagement,
-                requestorUid, requestorPackageName);
+                ssidPatternMatcher.getPath(), ssidPatternMatcher.getType(), bssidPatternMatcher,
+                wifiConfiguration.allowedKeyManagement);
     }
 
     @Override
@@ -582,9 +535,7 @@
                 && Objects.equals(this.bssidPatternMatcher,
                     lhs.bssidPatternMatcher)
                 && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
-                    lhs.wifiConfiguration.allowedKeyManagement)
-                && requestorUid == lhs.requestorUid
-                && TextUtils.equals(requestorPackageName, lhs.requestorPackageName);
+                    lhs.wifiConfiguration.allowedKeyManagement);
     }
 
     @Override
@@ -595,8 +546,6 @@
                 .append(", BSSID Match pattern=").append(bssidPatternMatcher)
                 .append(", SSID=").append(wifiConfiguration.SSID)
                 .append(", BSSID=").append(wifiConfiguration.BSSID)
-                .append(", requestorUid=").append(requestorUid)
-                .append(", requestorPackageName=").append(requestorPackageName)
                 .append("]")
                 .toString();
     }
@@ -618,12 +567,4 @@
         // not make much sense!
         return equals(other);
     }
-
-    /** @hide */
-    @Override
-    public void assertValidFromUid(int requestorUid) {
-        if (this.requestorUid != requestorUid) {
-            throw new SecurityException("mismatched UIDs");
-        }
-    }
 }
diff --git a/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
similarity index 71%
rename from wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
rename to wifi/java/android/net/wifi/WifiOemMigrationHook.java
index 642dcb9..22d7786 100755
--- a/wifi/java/android/net/wifi/WifiOemConfigStoreMigrationHook.java
+++ b/wifi/java/android/net/wifi/WifiOemMigrationHook.java
@@ -28,30 +28,17 @@
 
 /**
  * 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>
+ * data and other settings to the wifi mainline module.
  * @hide
  */
 @SystemApi
-public final class WifiOemConfigStoreMigrationHook {
+public final class WifiOemMigrationHook {
     /**
      * Container for all the wifi config data to migrate.
      */
-    public static final class MigrationData implements Parcelable {
+    public static final class ConfigStoreMigrationData implements Parcelable {
         /**
-         * Builder to create instance of {@link MigrationData}.
+         * Builder to create instance of {@link ConfigStoreMigrationData}.
          */
         public static final class Builder {
             private List<WifiConfiguration> mUserSavedNetworkConfigurations;
@@ -92,39 +79,40 @@
             }
 
             /**
-             * Build an instance of {@link MigrationData}.
+             * Build an instance of {@link ConfigStoreMigrationData}.
              *
-             * @return Instance of {@link MigrationData}.
+             * @return Instance of {@link ConfigStoreMigrationData}.
              */
-            public @NonNull MigrationData build() {
-                return new MigrationData(mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
+            public @NonNull ConfigStoreMigrationData build() {
+                return new ConfigStoreMigrationData(
+                        mUserSavedNetworkConfigurations, mUserSoftApConfiguration);
             }
         }
 
         private final List<WifiConfiguration> mUserSavedNetworkConfigurations;
         private final SoftApConfiguration mUserSoftApConfiguration;
 
-        private MigrationData(
+        private ConfigStoreMigrationData(
                 @Nullable List<WifiConfiguration> userSavedNetworkConfigurations,
                 @Nullable SoftApConfiguration userSoftApConfiguration) {
             mUserSavedNetworkConfigurations = userSavedNetworkConfigurations;
             mUserSoftApConfiguration = userSoftApConfiguration;
         }
 
-        public static final @NonNull Parcelable.Creator<MigrationData> CREATOR =
-                new Parcelable.Creator<MigrationData>() {
+        public static final @NonNull Parcelable.Creator<ConfigStoreMigrationData> CREATOR =
+                new Parcelable.Creator<ConfigStoreMigrationData>() {
                     @Override
-                    public MigrationData createFromParcel(Parcel in) {
+                    public ConfigStoreMigrationData createFromParcel(Parcel in) {
                         List<WifiConfiguration> userSavedNetworkConfigurations =
                                 in.readArrayList(null);
                         SoftApConfiguration userSoftApConfiguration = in.readParcelable(null);
-                        return new MigrationData(
+                        return new ConfigStoreMigrationData(
                                 userSavedNetworkConfigurations, userSoftApConfiguration);
                     }
 
                     @Override
-                    public MigrationData[] newArray(int size) {
-                        return new MigrationData[size];
+                    public ConfigStoreMigrationData[] newArray(int size) {
+                        return new ConfigStoreMigrationData[size];
                     }
                 };
 
@@ -164,16 +152,29 @@
         }
     }
 
-    private WifiOemConfigStoreMigrationHook() { }
+    private WifiOemMigrationHook() { }
 
     /**
      * Load data from OEM's config store.
+     * <p>
+     * Note:
+     * <li> OEM's need to implement {@link #loadFromConfigStore()} ()} 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 #loadFromConfigStore()} 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 #loadFromConfigStore()} occurs when a previously released
+     * device upgrades to the wifi mainline module from an OEM implementation of the wifi stack.
+     * </li>
      *
-     * @return Instance of {@link MigrationData} for migrating data, null if no
+     * @return Instance of {@link ConfigStoreMigrationData} for migrating data, null if no
      * migration is necessary.
      */
     @Nullable
-    public static MigrationData load() {
+    public static ConfigStoreMigrationData loadFromConfigStore() {
         // 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/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index c667334..a4b3e86 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -143,12 +143,6 @@
     }
 
     @Override
-    public void assertValidFromUid(int requestorUid) {
-        throw new SecurityException(
-                "WifiAwareAgentNetworkSpecifier should not be used in network requests");
-    }
-
-    @Override
     public NetworkSpecifier redact() {
         return null;
     }
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 81bf81e..2ebaa18 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -34,7 +34,6 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -447,8 +446,7 @@
                 pmk,
                 passphrase,
                 0, // no port info for deprecated IB APIs
-                -1, // no transport info for deprecated IB APIs
-                Process.myUid());
+                -1); // no transport info for deprecated IB APIs
     }
 
     /** @hide */
@@ -488,8 +486,7 @@
                 pmk,
                 passphrase,
                 0, // no port info for OOB APIs
-                -1, // no transport protocol info for OOB APIs
-                Process.myUid());
+                -1); // no transport protocol info for OOB APIs
     }
 
     private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index 5a4ed3c..65ac1ab 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -23,7 +23,6 @@
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.Process;
 import android.text.TextUtils;
 
 import java.util.Arrays;
@@ -144,19 +143,9 @@
      */
     public final int transportProtocol;
 
-    /**
-     * The UID of the process initializing this network specifier. Validated by receiver using
-     * checkUidIfNecessary() and is used by satisfiedBy() to determine whether matches the
-     * offered network.
-     *
-     * @hide
-     */
-    public final int requestorUid;
-
     /** @hide */
     public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
-            byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol,
-            int requestorUid) {
+            byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol) {
         this.type = type;
         this.role = role;
         this.clientId = clientId;
@@ -167,7 +156,6 @@
         this.passphrase = passphrase;
         this.port = port;
         this.transportProtocol = transportProtocol;
-        this.requestorUid = requestorUid;
     }
 
     public static final @android.annotation.NonNull Creator<WifiAwareNetworkSpecifier> CREATOR =
@@ -184,8 +172,7 @@
                         in.createByteArray(), // pmk
                         in.readString(), // passphrase
                         in.readInt(), // port
-                        in.readInt(), // transportProtocol
-                        in.readInt()); // requestorUid
+                        in.readInt()); // transportProtocol
                 }
 
                 @Override
@@ -221,7 +208,6 @@
         dest.writeString(passphrase);
         dest.writeInt(port);
         dest.writeInt(transportProtocol);
-        dest.writeInt(requestorUid);
     }
 
     /** @hide */
@@ -238,7 +224,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac),
-                Arrays.hashCode(pmk), passphrase, port, transportProtocol, requestorUid);
+                Arrays.hashCode(pmk), passphrase, port, transportProtocol);
     }
 
     /** @hide */
@@ -263,8 +249,7 @@
                 && Arrays.equals(pmk, lhs.pmk)
                 && Objects.equals(passphrase, lhs.passphrase)
                 && port == lhs.port
-                && transportProtocol == lhs.transportProtocol
-                && requestorUid == lhs.requestorUid;
+                && transportProtocol == lhs.transportProtocol;
     }
 
     /** @hide */
@@ -283,19 +268,11 @@
                 // masking PII
                 .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>")
                 .append(", port=").append(port).append(", transportProtocol=")
-                .append(transportProtocol).append(", requestorUid=").append(requestorUid)
+                .append(transportProtocol)
                 .append("]");
         return sb.toString();
     }
 
-    /** @hide */
-    @Override
-    public void assertValidFromUid(int requestorUid) {
-        if (this.requestorUid != requestorUid) {
-            throw new SecurityException("mismatched UIDs");
-        }
-    }
-
     /**
      * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a
      * peer.
@@ -463,7 +440,7 @@
             return new WifiAwareNetworkSpecifier(
                     WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role,
                     mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId,
-                    null, mPmk, mPskPassphrase, mPort, mTransportProtocol, Process.myUid());
+                    null, mPmk, mPskPassphrase, mPort, mTransportProtocol);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 36c7213..d479892 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -17,6 +17,7 @@
 package android.net.wifi.p2p;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -133,6 +134,7 @@
      *
      * By default this field is set to {@link #GROUP_OWNER_INTENT_AUTO}.
      */
+    @IntRange(from = 0, to = 15)
     public int groupOwnerIntent = GROUP_OWNER_INTENT_AUTO;
 
     /** @hide */
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
index cdb2806..8a86311 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -22,7 +22,9 @@
 import android.os.Parcelable;
 import android.util.LruCache;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 
@@ -78,8 +80,8 @@
      * Get the list of P2P groups.
      */
     @NonNull
-    public Collection<WifiP2pGroup> getGroupList() {
-        return mGroups.snapshot().values();
+    public List<WifiP2pGroup> getGroupList() {
+        return new ArrayList<>(mGroups.snapshot().values());
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 0fe0675..9c2cad9 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -326,6 +326,12 @@
 
     /**
      * Broadcast intent action indicating that remembered persistent groups have changed.
+     *
+     * You can <em>not</em> receive this through components declared
+     * in manifests, only by explicitly registering for it with
+     * {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver,
+     * android.content.IntentFilter) Context.registerReceiver()}.
+     *
      * @hide
      */
     @SystemApi
@@ -1347,20 +1353,33 @@
     }
 
     /**
-     * Force p2p to enter or exit listen state
+     * Force p2p to enter listen state
      *
      * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
-     * @param enable enables or disables listening
      * @param listener for callbacks on success or failure. Can be null.
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public void listen(@NonNull Channel c, boolean enable, @Nullable ActionListener listener) {
+    public void startListening(@NonNull Channel c, @Nullable ActionListener listener) {
         checkChannel(c);
-        c.mAsyncChannel.sendMessage(enable ? START_LISTEN : STOP_LISTEN,
-                0, c.putListener(listener));
+        c.mAsyncChannel.sendMessage(START_LISTEN, 0, c.putListener(listener));
+    }
+
+    /**
+     * Force p2p to exit listen state
+     *
+     * @param c is the channel created at {@link #initialize(Context, Looper, ChannelListener)}
+     * @param listener for callbacks on success or failure. Can be null.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void stopListening(@NonNull Channel c, @Nullable ActionListener listener) {
+        checkChannel(c);
+        c.mAsyncChannel.sendMessage(STOP_LISTEN, 0, c.putListener(listener));
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index 5484d24..e399b5b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -17,6 +17,7 @@
 package android.net.wifi.p2p;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -34,7 +35,7 @@
  */
 public final class WifiP2pWfdInfo implements Parcelable {
 
-    private boolean mWfdEnabled;
+    private boolean mEnabled;
 
     /** Device information bitmap */
     private int mDeviceInfo;
@@ -85,15 +86,15 @@
     /** @hide */
     @UnsupportedAppUsage
     public WifiP2pWfdInfo(int devInfo, int ctrlPort, int maxTput) {
-        mWfdEnabled = true;
+        mEnabled = true;
         mDeviceInfo = devInfo;
         mCtrlPort = ctrlPort;
         mMaxThroughput = maxTput;
     }
 
     /** Returns true is Wifi Display is enabled, false otherwise. */
-    public boolean isWfdEnabled() {
-        return mWfdEnabled;
+    public boolean isEnabled() {
+        return mEnabled;
     }
 
     /**
@@ -101,8 +102,8 @@
      *
      * @param enabled true to enable Wifi Display, false to disable
      */
-    public void setWfdEnabled(boolean enabled) {
-        mWfdEnabled = enabled;
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
     }
 
     /**
@@ -177,12 +178,12 @@
     }
 
     /** Sets the TCP port at which the WFD Device listens for RTSP messages. */
-    public void setControlPort(int port) {
+    public void setControlPort(@IntRange(from = 0) int port) {
         mCtrlPort = port;
     }
 
     /** Sets the maximum average throughput capability of the WFD Device, in megabits/second. */
-    public void setMaxThroughput(int maxThroughput) {
+    public void setMaxThroughput(@IntRange(from = 0) int maxThroughput) {
         mMaxThroughput = maxThroughput;
     }
 
@@ -200,7 +201,7 @@
     @Override
     public String toString() {
         StringBuffer sbuf = new StringBuffer();
-        sbuf.append("WFD enabled: ").append(mWfdEnabled);
+        sbuf.append("WFD enabled: ").append(mEnabled);
         sbuf.append("WFD DeviceInfo: ").append(mDeviceInfo);
         sbuf.append("\n WFD CtrlPort: ").append(mCtrlPort);
         sbuf.append("\n WFD MaxThroughput: ").append(mMaxThroughput);
@@ -215,7 +216,7 @@
     /** Copy constructor. */
     public WifiP2pWfdInfo(@Nullable WifiP2pWfdInfo source) {
         if (source != null) {
-            mWfdEnabled = source.mWfdEnabled;
+            mEnabled = source.mEnabled;
             mDeviceInfo = source.mDeviceInfo;
             mCtrlPort = source.mCtrlPort;
             mMaxThroughput = source.mMaxThroughput;
@@ -225,14 +226,14 @@
     /** Implement the Parcelable interface */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mWfdEnabled ? 1 : 0);
+        dest.writeInt(mEnabled ? 1 : 0);
         dest.writeInt(mDeviceInfo);
         dest.writeInt(mCtrlPort);
         dest.writeInt(mMaxThroughput);
     }
 
     private void readFromParcel(Parcel in) {
-        mWfdEnabled = (in.readInt() == 1);
+        mEnabled = (in.readInt() == 1);
         mDeviceInfo = in.readInt();
         mCtrlPort = in.readInt();
         mMaxThroughput = in.readInt();
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
deleted file mode 100644
index 060c85c..0000000
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ /dev/null
@@ -1,641 +0,0 @@
-/**
- * Copyright (c) 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License") {
- *  throw new UnsupportedOperationException();
- }
- * 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.wifi;
-
-import android.content.pm.ParceledListSlice;
-import android.net.DhcpInfo;
-import android.net.Network;
-import android.net.wifi.IActionListener;
-import android.net.wifi.IDppCallback;
-import android.net.wifi.ILocalOnlyHotspotCallback;
-import android.net.wifi.INetworkRequestMatchCallback;
-import android.net.wifi.IOnWifiActivityEnergyInfoListener;
-import android.net.wifi.IOnWifiUsabilityStatsListener;
-import android.net.wifi.IScanResultsCallback;
-import android.net.wifi.ISoftApCallback;
-import android.net.wifi.ISuggestionConnectionStatusListener;
-import android.net.wifi.ITrafficStateCallback;
-import android.net.wifi.ITxPacketCountListener;
-import android.net.wifi.IWifiConnectedNetworkScorer;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNetworkSuggestion;
-import android.net.wifi.hotspot2.IProvisioningCallback;
-import android.net.wifi.hotspot2.OsuProvider;
-import android.net.wifi.hotspot2.PasspointConfiguration;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.WorkSource;
-import android.os.connectivity.WifiActivityEnergyInfo;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions.
- *
- * This class is meant to be extended by real implementations of IWifiManager in order to facilitate
- * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
- * deprecated APIs, or the migration of existing API signatures.
- *
- * When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl
- * immediately and marked as @Deprecated first in this class. Children inheriting this class are
- * then given a short grace period to update themselves before the @Deprecated stub is removed for
- * good. If the API scheduled for removal has a replacement or an overload (signature change),
- * these should be introduced before the stub is removed to allow children to migrate.
- *
- * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as
- * well otherwise compilation will fail.
- */
-public class BaseWifiService extends IWifiManager.Stub {
-
-    private static final String TAG = BaseWifiService.class.getSimpleName();
-
-    @Override
-    public long getSupportedFeatures() {
-        throw new UnsupportedOperationException();
-    }
-
-    /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */
-    @Deprecated
-    public WifiActivityEnergyInfo reportActivityInfo() {
-        throw new UnsupportedOperationException();
-    }
-
-    /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */
-    @Deprecated
-    public void requestActivityInfo(ResultReceiver result) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public ParceledListSlice getConfiguredNetworks(String packageName, String featureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Map<String, Map<Integer, List<ScanResult>>> getAllMatchingFqdnsForScanResults(
-            List<ScanResult> scanResults) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
-            List<ScanResult> scanResults) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
-            List<OsuProvider> osuProviders) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int addOrUpdateNetwork(WifiConfiguration config, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean addOrUpdatePasspointConfiguration(
-            PasspointConfiguration config, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean removePasspointConfiguration(String fqdn, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public List<PasspointConfiguration> getPasspointConfigurations(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void queryPasspointIcon(long bssid, String fileName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int matchProviderWithCurrentNetwork(String fqdn) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void deauthenticateNetwork(long holdoff, boolean ess) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean removeNetwork(int netId, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean enableNetwork(int netId, boolean disableOthers, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean disableNetwork(int netId, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void allowAutojoinGlobal(boolean choice) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void allowAutojoin(int netId, boolean choice) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setMeteredOverridePasspoint(String fqdn, int meteredOverride) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean startScan(String packageName, String featureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public List<ScanResult> getScanResults(String callingPackage, String callingFeatureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean disconnect(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean reconnect(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean reassociate(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public WifiInfo getConnectionInfo(String callingPackage, String callingFeatureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean setWifiEnabled(String packageName, boolean enable) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int getWifiEnabledState() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String getCountryCode() {
-        throw new UnsupportedOperationException();
-    }
-
-    /** @deprecated use {@link #is5GHzBandSupported} instead */
-    @Deprecated
-    public boolean isDualBandSupported() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean is5GHzBandSupported() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean is6GHzBandSupported() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isWifiStandardSupported(int standard) {
-        throw new UnsupportedOperationException();
-    }
-
-    /** @deprecated use {@link WifiManager#isStaApConcurrencySupported()} */
-    @Deprecated
-    public boolean needs5GHzToAnyApBandConversion() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public DhcpInfo getDhcpInfo() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isScanAlwaysAvailable() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean releaseWifiLock(IBinder lock) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void initializeMulticastFiltering() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean isMulticastEnabled() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void acquireMulticastLock(IBinder binder, String tag) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void releaseMulticastLock(String tag) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void updateInterfaceIpState(String ifaceName, int mode) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean startSoftAp(WifiConfiguration wifiConfig) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean startTetheredHotspot(SoftApConfiguration softApConfig) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean stopSoftAp() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName,
-            String featureId, SoftApConfiguration customConfig) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void stopLocalOnlyHotspot() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void stopWatchLocalOnlyHotspot() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int getWifiApEnabledState() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public WifiConfiguration getWifiApConfiguration() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public SoftApConfiguration getSoftApConfiguration() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void notifyUserOfApBandConversion(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void enableTdls(String remoteIPAddress, boolean enable) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String getCurrentNetworkWpsNfcConfigurationToken() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void enableVerboseLogging(int verbose) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int getVerboseLoggingLevel() {
-        throw new UnsupportedOperationException();
-    }
-
-    /** @deprecated use {@link #allowAutojoinGlobal(boolean)} instead */
-    @Deprecated
-    public void enableWifiConnectivityManager(boolean enabled) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void disableEphemeralNetwork(String SSID, String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void factoryReset(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Network getCurrentNetwork() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public byte[] retrieveBackupData() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void restoreBackupData(byte[] data) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public byte[] retrieveSoftApBackupData() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public SoftApConfiguration restoreSoftApBackupData(byte[] data) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void startSubscriptionProvisioning(
-            OsuProvider provider, IProvisioningCallback callback) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void registerSoftApCallback(
-            IBinder binder, ISoftApCallback callback, int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void unregisterSoftApCallback(int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void registerTrafficStateCallback(
-            IBinder binder, ITrafficStateCallback callback, int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void unregisterTrafficStateCallback(int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void registerNetworkRequestMatchCallback(
-            IBinder binder, INetworkRequestMatchCallback callback, int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void unregisterNetworkRequestMatchCallback(int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int addNetworkSuggestions(
-            List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName,
-            String callingFeatureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int removeNetworkSuggestions(
-            List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public List<WifiNetworkSuggestion> getNetworkSuggestions(String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public String[] getFactoryMacAddresses() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void setDeviceMobilityState(int state) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri,
-            int selectedNetworkId, int netRole, IDppCallback callback) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri,
-            IDppCallback callback) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void stopDppSession() throws RemoteException {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void addOnWifiUsabilityStatsListener(
-            IBinder binder, IOnWifiUsabilityStatsListener listener, int listenerIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void removeOnWifiUsabilityStatsListener(int listenerIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void connect(WifiConfiguration config, int netId, IBinder binder,
-            IActionListener callback, int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void save(WifiConfiguration config, IBinder binder, IActionListener callback,
-            int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void forget(int netId, IBinder binder, IActionListener callback,
-            int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void getTxPacketCount(String packageName, IBinder binder,
-            ITxPacketCountListener callback, int callbackIdentifier) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void registerScanResultsCallback(IScanResultsCallback callback) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void unregisterScanResultsCallback(IScanResultsCallback callback) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void registerSuggestionConnectionStatusListener(IBinder binder,
-            ISuggestionConnectionStatusListener listener,
-            int listenerIdentifier, String packageName, String featureId) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void unregisterSuggestionConnectionStatusListener(int listenerIdentifier,
-            String packageName) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public int calculateSignalLevel(int rssi) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser(
-            List<ScanResult> scanResults) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public boolean setWifiConnectedNetworkScorer(IBinder binder,
-            IWifiConnectedNetworkScorer scorer) {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void clearWifiConnectedNetworkScorer() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults(
-            List<WifiNetworkSuggestion> networkSuggestions,
-            List<ScanResult> scanResults,
-            String callingPackage, String callingFeatureId) {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 2efdd97..d958488 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -287,17 +287,43 @@
 
     @Test
     public void testToWifiConfigurationWithSupportedParameter() {
-        SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
+        SoftApConfiguration softApConfig_2g = new SoftApConfiguration.Builder()
+                .setPassphrase("secretsecret",
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+                .setChannel(11, SoftApConfiguration.BAND_2GHZ)
+                .setHiddenSsid(true)
+                .build();
+        WifiConfiguration wifiConfig_2g = softApConfig_2g.toWifiConfiguration();
+        assertThat(wifiConfig_2g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        assertThat(wifiConfig_2g.preSharedKey).isEqualTo("secretsecret");
+        assertThat(wifiConfig_2g.apBand).isEqualTo(WifiConfiguration.AP_BAND_2GHZ);
+        assertThat(wifiConfig_2g.apChannel).isEqualTo(11);
+        assertThat(wifiConfig_2g.hiddenSSID).isEqualTo(true);
+
+        SoftApConfiguration softApConfig_5g = new SoftApConfiguration.Builder()
                 .setPassphrase("secretsecret",
                         SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setChannel(149, SoftApConfiguration.BAND_5GHZ)
                 .setHiddenSsid(true)
                 .build();
-        WifiConfiguration wifiConfig = softApConfig.toWifiConfiguration();
-        assertThat(wifiConfig.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
-        assertThat(wifiConfig.preSharedKey).isEqualTo("secretsecret");
-        assertThat(wifiConfig.apBand).isEqualTo(WifiConfiguration.AP_BAND_5GHZ);
-        assertThat(wifiConfig.apChannel).isEqualTo(149);
-        assertThat(wifiConfig.hiddenSSID).isEqualTo(true);
+        WifiConfiguration wifiConfig_5g = softApConfig_5g.toWifiConfiguration();
+        assertThat(wifiConfig_5g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        assertThat(wifiConfig_5g.preSharedKey).isEqualTo("secretsecret");
+        assertThat(wifiConfig_5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_5GHZ);
+        assertThat(wifiConfig_5g.apChannel).isEqualTo(149);
+        assertThat(wifiConfig_5g.hiddenSSID).isEqualTo(true);
+
+        SoftApConfiguration softApConfig_2g5g = new SoftApConfiguration.Builder()
+                .setPassphrase("secretsecret",
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+                .setBand(SoftApConfiguration.BAND_2GHZ | SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+        WifiConfiguration wifiConfig_2g5g = softApConfig_2g5g.toWifiConfiguration();
+        assertThat(wifiConfig_2g5g.getAuthType()).isEqualTo(WifiConfiguration.KeyMgmt.WPA2_PSK);
+        assertThat(wifiConfig_2g5g.preSharedKey).isEqualTo("secretsecret");
+        assertThat(wifiConfig_2g5g.apBand).isEqualTo(WifiConfiguration.AP_BAND_ANY);
+        assertThat(wifiConfig_2g5g.apChannel).isEqualTo(0);
+        assertThat(wifiConfig_2g5g.hiddenSSID).isEqualTo(true);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index a189d50..53e9755 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1867,7 +1867,7 @@
      * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync}
      * throws an exception.
      */
-    @Test(expected = IllegalArgumentException.class)
+    @Test(expected = NullPointerException.class)
     public void testGetWifiActivityInfoNullExecutor() throws Exception {
         mWifiManager.getWifiActivityEnergyInfoAsync(null, mOnWifiActivityEnergyInfoListener);
     }
@@ -1876,7 +1876,7 @@
      * Tests that passing a null listener to {@link WifiManager#getWifiActivityEnergyInfoAsync}
      * throws an exception.
      */
-    @Test(expected = IllegalArgumentException.class)
+    @Test(expected = NullPointerException.class)
     public void testGetWifiActivityInfoNullListener() throws Exception {
         mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, null);
     }
@@ -2380,4 +2380,14 @@
         verify(mWifiConnectedNetworkScorer).start(0);
         verify(mWifiConnectedNetworkScorer).stop(10);
     }
+
+    @Test
+    public void testScanThrottle() throws Exception {
+        mWifiManager.setScanThrottleEnabled(true);
+        verify(mWifiService).setScanThrottleEnabled(true);
+
+        when(mWifiService.isScanThrottleEnabled()).thenReturn(false);
+        assertFalse(mWifiManager.isScanThrottleEnabled());
+        verify(mWifiService).isScanThrottleEnabled();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index adc41f0..0233ee2 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -22,7 +22,6 @@
 
 import android.net.MacAddress;
 import android.net.MatchAllNetworkSpecifier;
-import android.net.NetworkRequest;
 import android.os.Parcel;
 import android.os.PatternMatcher;
 import android.util.Pair;
@@ -36,10 +35,6 @@
  */
 @SmallTest
 public class WifiNetworkAgentSpecifierTest {
-    private static final int TEST_UID = 5;
-    private static final int TEST_UID_1 = 8;
-    private static final String TEST_PACKAGE = "com.test";
-    private static final String TEST_PACKAGE_1 = "com.test.1";
     private static final String TEST_SSID = "Test123";
     private static final String TEST_SSID_PATTERN = "Test";
     private static final String TEST_SSID_1 = "456test";
@@ -71,16 +66,6 @@
     }
 
     /**
-     * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps.
-     */
-    @Test(expected = IllegalStateException.class)
-    public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() {
-        WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
-
-        specifier.assertValidFromUid(TEST_UID);
-    }
-
-    /**
      * Validate NetworkAgentSpecifier equals with itself.
      * a) Create network agent specifier 1 for WPA_PSK network
      * b) Create network agent specifier 2 with the same params as specifier 1.
@@ -105,15 +90,13 @@
         WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
         WifiNetworkAgentSpecifier specifier1 =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfiguration1,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfiguration1);
 
         WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
         wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkAgentSpecifier specifier2 =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfiguration2,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfiguration2);
 
         assertFalse(specifier2.equals(specifier1));
     }
@@ -129,15 +112,13 @@
         WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
         WifiNetworkAgentSpecifier specifier1 =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfiguration1,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfiguration1);
 
         WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
         wifiConfiguration2.SSID = TEST_SSID_1;
         WifiNetworkAgentSpecifier specifier2 =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfiguration2,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfiguration2);
 
         assertFalse(specifier2.equals(specifier1));
     }
@@ -153,15 +134,13 @@
         WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration();
         WifiNetworkAgentSpecifier specifier1 =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfiguration1,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfiguration1);
 
         WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1);
         wifiConfiguration2.BSSID = TEST_BSSID_1;
         WifiNetworkAgentSpecifier specifier2 =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfiguration2,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfiguration2);
 
         assertFalse(specifier2.equals(specifier1));
     }
@@ -215,8 +194,7 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -244,8 +222,7 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -273,8 +250,7 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -293,8 +269,7 @@
         wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID_1 + "\"";
         WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfigurationNetworkAgent,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfigurationNetworkAgent);
 
         PatternMatcher ssidPattern =
                 new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
@@ -306,8 +281,7 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -326,8 +300,7 @@
         wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1;
         WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfigurationNetworkAgent,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfigurationNetworkAgent);
 
         PatternMatcher ssidPattern =
                 new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB);
@@ -340,8 +313,7 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -360,8 +332,7 @@
         wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1;
         WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier =
                 new WifiNetworkAgentSpecifier(
-                        wifiConfigurationNetworkAgent,
-                        TEST_UID, TEST_PACKAGE);
+                        wifiConfigurationNetworkAgent);
 
         PatternMatcher ssidPattern =
                 new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
@@ -374,8 +345,7 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
@@ -402,41 +372,12 @@
         WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
                 ssidPattern,
                 bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID, TEST_PACKAGE);
+                wificonfigurationNetworkSpecifier);
 
         assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
         assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
     }
 
-    /**
-     * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching.
-     * a) Create network agent specifier for WPA_PSK network
-     * b) Create network specifier with matching SSID and BSSID pattern, but different UID.
-     * c) Ensure that the agent specifier is not satisfied by specifier.
-     */
-    @Test
-    public void
-            testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentUid() {
-        WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier();
-
-        PatternMatcher ssidPattern =
-                new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
-        Pair<MacAddress, MacAddress> bssidPattern =
-                Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
-                        MacAddress.fromString(TEST_BSSID_OUI_MASK));
-        WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
-        wificonfigurationNetworkSpecifier.allowedKeyManagement
-                .set(WifiConfiguration.KeyMgmt.WPA_PSK);
-        WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier(
-                ssidPattern,
-                bssidPattern,
-                wificonfigurationNetworkSpecifier,
-                TEST_UID_1, TEST_PACKAGE_1);
-
-        assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier));
-        assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier));
-    }
 
     private WifiConfiguration createDefaultWifiConfiguration() {
         WifiConfiguration wifiConfiguration = new WifiConfiguration();
@@ -448,8 +389,7 @@
     }
 
     private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() {
-        return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID,
-                TEST_PACKAGE);
+        return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration());
     }
 
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index 1619744..3b67236 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -29,7 +29,6 @@
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
 import android.os.PatternMatcher;
-import android.os.Process;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
@@ -41,8 +40,6 @@
  */
 @SmallTest
 public class WifiNetworkSpecifierTest {
-    private static final int TEST_UID = 5;
-    private static final String TEST_PACKAGE_NAME = "com.test";
     private static final String TEST_SSID = "Test123";
     private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00";
     private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00";
@@ -62,7 +59,6 @@
         assertTrue(specifier instanceof WifiNetworkSpecifier);
         WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier;
 
-        assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid);
         assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
         assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType());
         assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS,
@@ -367,8 +363,7 @@
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         Parcel parcelW = Parcel.obtain();
         specifier.writeToParcel(parcelW, 0);
@@ -399,8 +394,7 @@
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         assertTrue(specifier.satisfiedBy(null));
         assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier()));
@@ -422,15 +416,13 @@
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         WifiNetworkSpecifier specifier2 =
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         assertTrue(specifier2.satisfiedBy(specifier1));
     }
@@ -451,8 +443,7 @@
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration1,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration1);
 
         WifiConfiguration wifiConfiguration2 = new WifiConfiguration();
         wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
@@ -460,8 +451,7 @@
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration2,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration2);
 
         assertFalse(specifier2.satisfiedBy(specifier1));
     }
@@ -482,15 +472,13 @@
                 new WifiNetworkSpecifier(new PatternMatcher("", PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         WifiNetworkSpecifier specifier2 =
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         assertFalse(specifier2.satisfiedBy(specifier1));
     }
@@ -511,44 +499,13 @@
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
                                 MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
+                        wifiConfiguration);
 
         WifiNetworkSpecifier specifier2 =
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
                         Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS,
                                 WifiManager.ALL_ZEROS_MAC_ADDRESS),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
-
-        assertFalse(specifier2.satisfiedBy(specifier1));
-    }
-
-    /**
-     * Validate NetworkSpecifier matching.
-     * a) Create network specifier 1 for WPA_PSK network
-     * b) Create network specifier 2 with different package name .
-     * c) Ensure that the specifier 2 is not satisfied by specifier 1.
-     */
-    @Test
-    public void testWifiNetworkSpecifierDoesNotSatisfyWhenPackageNameDifferent() {
-        WifiConfiguration wifiConfiguration = new WifiConfiguration();
-        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
-        wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY;
-
-        WifiNetworkSpecifier specifier1 =
-                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
-                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
-                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME);
-
-        WifiNetworkSpecifier specifier2 =
-                new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
-                        Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS),
-                                MacAddress.fromString(TEST_BSSID_OUI_MASK)),
-                        wifiConfiguration,
-                        TEST_UID, TEST_PACKAGE_NAME + "blah");
+                        wifiConfiguration);
 
         assertFalse(specifier2.satisfiedBy(specifier1));
     }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index c3b6285..81b02fa 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -162,17 +162,6 @@
         collector.checkThat("Match unexpected", oldNs.satisfiedBy(newNs), equalTo(false));
     }
 
-    /**
-     * Validate that agent network specifier cannot be used as in network requests - i.e. that
-     * throws an exception when queried for UID validity.
-     */
-    @Test(expected = SecurityException.class)
-    public void testNoUsageInRequest() {
-        WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier();
-
-        dut.assertValidFromUid(0);
-    }
-
     // utilities
 
     /**
@@ -182,6 +171,6 @@
     WifiAwareNetworkSpecifier getDummyNetworkSpecifier(int clientId) {
         return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
                 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6],
-                null, null, 10, 5, 0);
+                null, null, 10, 5);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 65fbf5b..c5f9804 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -1564,7 +1564,7 @@
         WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(NETWORK_SPECIFIER_TYPE_IB,
                 WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, 5, 568, 334,
                 HexEncoding.decode("000102030405".toCharArray(), false),
-                "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4, 10001);
+                "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4);
 
         Parcel parcelW = Parcel.obtain();
         ns.writeToParcel(parcelW, 0);
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
index 17ee755..6edc287 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java
@@ -45,7 +45,7 @@
         assertEquals(devA.groupCapability, devB.groupCapability);
         assertEquals(devA.status, devB.status);
         if (devA.wfdInfo != null) {
-            assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled());
+            assertEquals(devA.wfdInfo.isEnabled(), devB.wfdInfo.isEnabled());
             assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex());
             assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort());
             assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput());
diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
index 15a0aac..2a9b36b 100644
--- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pWfdInfoTest.java
@@ -55,8 +55,8 @@
     public void testSettersGetters() throws Exception {
         WifiP2pWfdInfo info = new WifiP2pWfdInfo();
 
-        info.setWfdEnabled(true);
-        assertTrue(info.isWfdEnabled());
+        info.setEnabled(true);
+        assertTrue(info.isEnabled());
 
         info.setDeviceType(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE);
         assertEquals(WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE, info.getDeviceType());