Merge "UI for MANAGE_EXTERNAL_STORAGE: Add settings enums"
diff --git a/Android.bp b/Android.bp
index 4b82e1d..bba6185 100644
--- a/Android.bp
+++ b/Android.bp
@@ -284,6 +284,7 @@
     name: "framework-aidl-export-defaults",
     aidl: {
         export_include_dirs: [
+            "apex/media/framework/java",
             "core/java",
             "drm/java",
             "graphics/java",
@@ -291,7 +292,6 @@
             "location/java",
             "lowpan/java",
             "media/java",
-            "media/apex/java",
             "media/mca/effect/java",
             "media/mca/filterfw/java",
             "media/mca/filterpacks/java",
@@ -451,6 +451,16 @@
     // For backwards compatibility.
     stem: "framework",
     apex_available: ["//apex_available:platform"],
+    visibility: [
+        "//frameworks/base",
+        // TODO(b/144149403) remove the below lines
+        "//frameworks/base/apex/appsearch/framework",
+        "//frameworks/base/apex/blobstore/framework",
+        "//frameworks/base/apex/jobscheduler/framework",
+        "//frameworks/base/apex/statsd/service",
+        "//frameworks/base/wifi",
+        "//frameworks/opt/net/wifi/service",
+    ],
 }
 
 // This "framework" module is NOT installed to the device. It's
@@ -622,6 +632,7 @@
         "core/java/com/android/internal/util/StateMachine.java",
         "core/java/com/android/internal/util/TrafficStatsConstants.java",
         "core/java/com/android/internal/util/WakeupMessage.java",
+        "core/java/com/android/internal/util/TokenBucket.java",
         "core/java/android/net/shared/*.java",
     ],
 }
@@ -631,13 +642,13 @@
     name: "framework-tethering-shared-srcs",
     srcs: [
         "core/java/android/util/LocalLog.java",
-        "core/java/com/android/internal/util/BitUtils.java",
         "core/java/com/android/internal/util/IndentingPrintWriter.java",
         "core/java/com/android/internal/util/IState.java",
         "core/java/com/android/internal/util/MessageUtils.java",
         "core/java/com/android/internal/util/Preconditions.java",
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
+        "core/java/android/net/shared/Inet4AddressUtils.java",
     ],
 }
 
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 78e72bf..4d1ac0e 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/hid/
                cmds/input/
+               core/jni/
                libs/input/
 
 [Hook Scripts]
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 78f1b9c..d195047 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -217,21 +217,6 @@
     defaults: ["framework-stubs-default"],
 }
 
-java_system_modules {
-    name: "android_stubs_current_system_modules",
-    libs: ["android_stubs_current"],
-}
-
-java_system_modules {
-    name: "android_system_stubs_current_system_modules",
-    libs: ["android_system_stubs_current"],
-}
-
-java_system_modules {
-    name: "android_test_stubs_current_system_modules",
-    libs: ["android_test_stubs_current"],
-}
-
 /////////////////////////////////////////////////////////////////////
 // hwbinder.stubs provides APIs required for building HIDL Java
 // libraries.
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 1ec96ec..9310762 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -81,7 +81,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
 import com.android.server.AppStateTracker;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.FgThread;
@@ -117,6 +116,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -1283,7 +1283,7 @@
         super(context);
 
         mLocalPM = LocalServices.getService(PackageManagerInternal.class);
-        mActivityManagerInternal = Preconditions.checkNotNull(
+        mActivityManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(ActivityManagerInternal.class));
 
         mHandler = new JobHandler(context.getMainLooper());
@@ -1389,7 +1389,7 @@
                 controller.onSystemServicesReady();
             }
 
-            mAppStateTracker = Preconditions.checkNotNull(
+            mAppStateTracker = Objects.requireNonNull(
                     LocalServices.getService(AppStateTracker.class));
 
             // Register br for package removals and user removals.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index 8b61006..aa3d74a 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -23,7 +23,6 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
 import com.android.server.AppStateTracker;
 import com.android.server.AppStateTracker.Listener;
 import com.android.server.LocalServices;
@@ -32,6 +31,7 @@
 import com.android.server.job.StateControllerProto;
 import com.android.server.job.StateControllerProto.BackgroundJobsController.TrackedJob;
 
+import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -59,7 +59,7 @@
     public BackgroundJobsController(JobSchedulerService service) {
         super(service);
 
-        mAppStateTracker = Preconditions.checkNotNull(
+        mAppStateTracker = Objects.requireNonNull(
                 LocalServices.getService(AppStateTracker.class));
         mAppStateTracker.addListener(mForceAppStandbyListener);
     }
diff --git a/apex/media/OWNERS b/apex/media/OWNERS
new file mode 100644
index 0000000..0ac750c
--- /dev/null
+++ b/apex/media/OWNERS
@@ -0,0 +1,4 @@
+andrewlewis@google.com
+dwkang@google.com
+marcone@google.com
+sungsoo@google.com
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
new file mode 100644
index 0000000..6bd0086
--- /dev/null
+++ b/apex/media/framework/Android.bp
@@ -0,0 +1,120 @@
+// 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.
+
+java_library {
+    name: "updatable-media",
+
+    srcs: [
+        ":updatable-media-srcs",
+    ],
+
+    aidl: {
+        export_include_dirs: [
+            "java",
+        ],
+
+        // It would be great if we don't need to add include_dirs for public
+        // parcelable classes. Find a better way.
+        include_dirs: [
+            // To refer:
+            // android.os.Bundle
+            // android.os.ResultReceiver
+            "frameworks/base/core/java",
+        ],
+    },
+
+    permitted_packages: [
+        "android.media",
+    ],
+
+    installable: true,
+
+    // TODO: build against stable API surface. Use core_platform for now to avoid
+    // link-check failure with exoplayer building against "current".
+    sdk_version: "core_platform",
+    libs: [
+        // The order matters. android_system_* library should come later.
+        "framework_media_annotation",
+        "android_system_stubs_current",
+    ],
+
+    static_libs: [
+        "exoplayer2-core"
+    ],
+    jarjar_rules: "jarjar_rules.txt",
+
+    plugins: ["java_api_finder"],
+}
+
+filegroup {
+    name: "updatable-media-srcs",
+    srcs: [
+        ":mediaparser-srcs",
+        ":mediasession2-srcs",
+    ],
+}
+
+filegroup {
+    name: "mediasession2-srcs",
+    srcs: [
+        "java/android/media/Controller2Link.java",
+        "java/android/media/IMediaController2.aidl",
+        "java/android/media/IMediaSession2.aidl",
+        "java/android/media/IMediaSession2Service.aidl",
+        "java/android/media/MediaConstants.java",
+        "java/android/media/MediaController2.java",
+        "java/android/media/MediaSession2.java",
+        "java/android/media/MediaSession2Service.java",
+        "java/android/media/Session2Command.java",
+        "java/android/media/Session2CommandGroup.java",
+        "java/android/media/Session2Link.java",
+        "java/android/media/Session2Token.java",
+    ],
+    path: "java",
+}
+
+filegroup {
+    name: "mediaparser-srcs",
+    srcs: [
+        "java/android/media/MediaParser.java"
+    ],
+    path: "java"
+}
+
+droidstubs {
+    name: "updatable-media-stubs",
+    srcs: [
+        ":updatable-media-srcs",
+        ":framework-media-annotation-srcs",
+    ],
+    defaults: [ "framework-module-stubs-defaults-systemapi" ],
+    aidl: {
+        // TODO(b/135922046) remove this
+        include_dirs: ["frameworks/base/core/java"],
+    },
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "updatable_media_stubs",
+    srcs: [":updatable-media-stubs"],
+    sdk_version: "system_current",
+}
+
+java_library {
+    name: "framework_media_annotation",
+    srcs: [":framework-media-annotation-srcs"],
+    installable: false,
+    sdk_version: "core_current",
+}
diff --git a/media/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
similarity index 100%
rename from media/jarjar_rules.txt
rename to apex/media/framework/jarjar_rules.txt
diff --git a/media/apex/java/android/media/BufferingParams.java b/apex/media/framework/java/android/media/BufferingParams.java
similarity index 97%
rename from media/apex/java/android/media/BufferingParams.java
rename to apex/media/framework/java/android/media/BufferingParams.java
index 943f142..04af028 100644
--- a/media/apex/java/android/media/BufferingParams.java
+++ b/apex/media/framework/java/android/media/BufferingParams.java
@@ -16,13 +16,9 @@
 
 package android.media;
 
-import android.annotation.IntDef;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Structure for source buffering management params.
  *
diff --git a/media/apex/java/android/media/Controller2Link.aidl b/apex/media/framework/java/android/media/Controller2Link.aidl
similarity index 100%
rename from media/apex/java/android/media/Controller2Link.aidl
rename to apex/media/framework/java/android/media/Controller2Link.aidl
diff --git a/media/apex/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java
similarity index 100%
rename from media/apex/java/android/media/Controller2Link.java
rename to apex/media/framework/java/android/media/Controller2Link.java
diff --git a/media/apex/java/android/media/DataSourceCallback.java b/apex/media/framework/java/android/media/DataSourceCallback.java
similarity index 100%
rename from media/apex/java/android/media/DataSourceCallback.java
rename to apex/media/framework/java/android/media/DataSourceCallback.java
diff --git a/media/apex/java/android/media/IMediaController2.aidl b/apex/media/framework/java/android/media/IMediaController2.aidl
similarity index 100%
rename from media/apex/java/android/media/IMediaController2.aidl
rename to apex/media/framework/java/android/media/IMediaController2.aidl
diff --git a/media/apex/java/android/media/IMediaSession2.aidl b/apex/media/framework/java/android/media/IMediaSession2.aidl
similarity index 100%
rename from media/apex/java/android/media/IMediaSession2.aidl
rename to apex/media/framework/java/android/media/IMediaSession2.aidl
diff --git a/media/apex/java/android/media/IMediaSession2Service.aidl b/apex/media/framework/java/android/media/IMediaSession2Service.aidl
similarity index 100%
rename from media/apex/java/android/media/IMediaSession2Service.aidl
rename to apex/media/framework/java/android/media/IMediaSession2Service.aidl
diff --git a/media/apex/java/android/media/MediaConstants.java b/apex/media/framework/java/android/media/MediaConstants.java
similarity index 100%
rename from media/apex/java/android/media/MediaConstants.java
rename to apex/media/framework/java/android/media/MediaConstants.java
diff --git a/media/apex/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
similarity index 100%
rename from media/apex/java/android/media/MediaController2.java
rename to apex/media/framework/java/android/media/MediaController2.java
diff --git a/media/apex/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
similarity index 100%
rename from media/apex/java/android/media/MediaParser.java
rename to apex/media/framework/java/android/media/MediaParser.java
diff --git a/media/apex/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
similarity index 100%
rename from media/apex/java/android/media/MediaSession2.java
rename to apex/media/framework/java/android/media/MediaSession2.java
diff --git a/media/apex/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
similarity index 100%
rename from media/apex/java/android/media/MediaSession2Service.java
rename to apex/media/framework/java/android/media/MediaSession2Service.java
diff --git a/media/apex/java/android/media/ProxyDataSourceCallback.java b/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
similarity index 100%
rename from media/apex/java/android/media/ProxyDataSourceCallback.java
rename to apex/media/framework/java/android/media/ProxyDataSourceCallback.java
diff --git a/media/apex/java/android/media/RoutingDelegate.java b/apex/media/framework/java/android/media/RoutingDelegate.java
similarity index 100%
rename from media/apex/java/android/media/RoutingDelegate.java
rename to apex/media/framework/java/android/media/RoutingDelegate.java
diff --git a/media/apex/java/android/media/Session2Command.aidl b/apex/media/framework/java/android/media/Session2Command.aidl
similarity index 100%
rename from media/apex/java/android/media/Session2Command.aidl
rename to apex/media/framework/java/android/media/Session2Command.aidl
diff --git a/media/apex/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
similarity index 100%
rename from media/apex/java/android/media/Session2Command.java
rename to apex/media/framework/java/android/media/Session2Command.java
diff --git a/media/apex/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
similarity index 98%
rename from media/apex/java/android/media/Session2CommandGroup.java
rename to apex/media/framework/java/android/media/Session2CommandGroup.java
index 0ee5f62..13aabfc 100644
--- a/media/apex/java/android/media/Session2CommandGroup.java
+++ b/apex/media/framework/java/android/media/Session2CommandGroup.java
@@ -38,8 +38,8 @@
 public final class Session2CommandGroup implements Parcelable {
     private static final String TAG = "Session2CommandGroup";
 
-    public static final @android.annotation.NonNull Parcelable.Creator<Session2CommandGroup> CREATOR =
-            new Parcelable.Creator<Session2CommandGroup>() {
+    public static final @android.annotation.NonNull Parcelable.Creator<Session2CommandGroup>
+            CREATOR = new Parcelable.Creator<Session2CommandGroup>() {
                 @Override
                 public Session2CommandGroup createFromParcel(Parcel in) {
                     return new Session2CommandGroup(in);
diff --git a/media/apex/java/android/media/Session2Link.java b/apex/media/framework/java/android/media/Session2Link.java
similarity index 100%
rename from media/apex/java/android/media/Session2Link.java
rename to apex/media/framework/java/android/media/Session2Link.java
diff --git a/media/apex/java/android/media/Session2Token.aidl b/apex/media/framework/java/android/media/Session2Token.aidl
similarity index 100%
rename from media/apex/java/android/media/Session2Token.aidl
rename to apex/media/framework/java/android/media/Session2Token.aidl
diff --git a/media/apex/java/android/media/Session2Token.java b/apex/media/framework/java/android/media/Session2Token.java
similarity index 95%
rename from media/apex/java/android/media/Session2Token.java
rename to apex/media/framework/java/android/media/Session2Token.java
index 6eb76b1..aae2e1b 100644
--- a/media/apex/java/android/media/Session2Token.java
+++ b/apex/media/framework/java/android/media/Session2Token.java
@@ -52,17 +52,18 @@
 public final class Session2Token implements Parcelable {
     private static final String TAG = "Session2Token";
 
-    public static final @android.annotation.NonNull Creator<Session2Token> CREATOR = new Creator<Session2Token>() {
-        @Override
-        public Session2Token createFromParcel(Parcel p) {
-            return new Session2Token(p);
-        }
+    public static final @android.annotation.NonNull Creator<Session2Token> CREATOR =
+            new Creator<Session2Token>() {
+                @Override
+                public Session2Token createFromParcel(Parcel p) {
+                    return new Session2Token(p);
+                }
 
-        @Override
-        public Session2Token[] newArray(int size) {
-            return new Session2Token[size];
-        }
-    };
+                @Override
+                public Session2Token[] newArray(int size) {
+                    return new Session2Token[size];
+                }
+            };
 
     /**
      * @hide
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index aed6ad9..f8325d4 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -18,6 +18,7 @@
 filegroup {
     name: "statsd_aidl",
     srcs: [
+        "android/os/IPendingIntentRef.aidl",
         "android/os/IPullAtomCallback.aidl",
         "android/os/IPullAtomResultReceiver.aidl",
         "android/os/IStatsCompanionService.aidl",
diff --git a/apex/statsd/aidl/android/os/IPendingIntentRef.aidl b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
new file mode 100644
index 0000000..6b9e467
--- /dev/null
+++ b/apex/statsd/aidl/android/os/IPendingIntentRef.aidl
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.StatsDimensionsValue;
+
+/**
+  * Binder interface to hold a PendingIntent for StatsCompanionService.
+  * {@hide}
+  */
+interface IPendingIntentRef {
+
+    /**
+     * Sends a broadcast to the specified PendingIntent that it should getData now.
+     * This should be only called from StatsCompanionService.
+     */
+     oneway void sendDataBroadcast(long lastReportTimeNs);
+
+    /**
+     * Send a broadcast to the specified PendingIntent notifying it that the list of active configs
+     * has changed. This should be only called from StatsCompanionService.
+     */
+     oneway void sendActiveConfigsChangedBroadcast(in long[] configIds);
+
+     /**
+      * Send a broadcast to the specified PendingIntent, along with the other information
+      * specified. This should only be called from StatsCompanionService.
+      */
+     oneway void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
+                                         long subscriptionRuleId, in String[] cookies,
+                                         in StatsDimensionsValue dimensionsValue);
+}
\ No newline at end of file
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index 5a6118e..21b7767 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -17,7 +17,6 @@
 package android.os;
 
 import android.os.IPullAtomCallback;
-import android.os.StatsDimensionsValue;
 import android.os.StatsLogEventWrapper;
 
 /**
@@ -66,24 +65,6 @@
     /** Pull the specified data. Results will be sent to statsd when complete. */
     StatsLogEventWrapper[] pullData(int pullCode);
 
-    /** Send a broadcast to the specified PendingIntent's as IBinder that it should getData now. */
-    oneway void sendDataBroadcast(in IBinder intentSender, long lastReportTimeNs);
-
-    /**
-     * Send a broadcast to the specified PendingIntent's as IBinder notifying it that the list
-     * of active configs has changed.
-     */
-    oneway void sendActiveConfigsChangedBroadcast(in IBinder intentSender, in long[] configIds);
-
-    /**
-     * Requests StatsCompanionService to send a broadcast using the given intentSender
-     * (which should cast to an IIntentSender), along with the other information specified.
-     */
-    oneway void sendSubscriberBroadcast(in IBinder intentSender, long configUid, long configId,
-                                        long subscriptionId, long subscriptionRuleId,
-                                        in String[] cookies,
-                                        in StatsDimensionsValue dimensionsValue);
-
     /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
     oneway void triggerUidSnapshot();
 
diff --git a/apex/statsd/aidl/android/os/IStatsManagerService.aidl b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
index 45ba3a2..05583677 100644
--- a/apex/statsd/aidl/android/os/IStatsManagerService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsManagerService.aidl
@@ -30,12 +30,19 @@
      * memory consumed by the metrics for this configuration approach the pre-defined limits. There
      * can be at most one listener per config key.
      *
-     * Requires Manifest.permission.DUMP.
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
      */
-    void setDataFetchOperation(long configKey, in PendingIntent pendingIntent,
+    void setDataFetchOperation(long configId, in PendingIntent pendingIntent,
         in String packageName);
 
     /**
+     * Removes the data fetch operation for the specified configuration.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    void removeDataFetchOperation(long configId, in String packageName);
+
+    /**
      * Registers the given pending intent for this packagename. This intent is invoked when the
      * active status of any of the configs sent by this package changes and will contain a list of
      * config ids that are currently active. It also returns the list of configs that are currently
@@ -46,6 +53,13 @@
     long[] setActiveConfigsChangedOperation(in PendingIntent pendingIntent, in String packageName);
 
     /**
+     * Removes the active configs changed operation for the specified package name.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    void removeActiveConfigsChangedOperation(in String packageName);
+
+    /**
      * Set the PendingIntent to be used when broadcasting subscriber
      * information to the given subscriberId within the given config.
      *
@@ -58,8 +72,17 @@
      * This function can only be called by the owner (uid) of the config. It must be called each
      * time statsd starts. Later calls overwrite previous calls; only one PendingIntent is stored.
      *
-     * Requires Manifest.permission.DUMP.
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
      */
     void setBroadcastSubscriber(long configKey, long subscriberId, in PendingIntent pendingIntent,
                                 in String packageName);
+
+    /**
+     * Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
+     * Any broadcasts associated with subscriberId will henceforth not be sent.
+     * No-op if this (configKey, subscriberId) pair was not associated with an PendingIntent.
+     *
+     * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
+     */
+    void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
 }
\ No newline at end of file
diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl
index cce79fa..9358439 100644
--- a/apex/statsd/aidl/android/os/IStatsd.aidl
+++ b/apex/statsd/aidl/android/os/IStatsd.aidl
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.os.IStatsPullerCallback;
+import android.os.IPendingIntentRef;
 import android.os.IPullAtomCallback;
 import android.os.ParcelFileDescriptor;
 
@@ -114,14 +115,15 @@
      *
      * Requires Manifest.permission.DUMP.
      */
-    void setDataFetchOperation(long configKey, in IBinder intentSender, in String packageName);
+    void setDataFetchOperation(long configId, in IPendingIntentRef pendingIntentRef,
+                               int callingUid);
 
     /**
      * Removes the data fetch operation for the specified configuration.
      *
      * Requires Manifest.permission.DUMP.
      */
-    void removeDataFetchOperation(long configKey, in String packageName);
+    void removeDataFetchOperation(long configId, int callingUid);
 
     /**
      * Registers the given pending intent for this packagename. This intent is invoked when the
@@ -131,14 +133,14 @@
      *
      * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
      */
-    long[] setActiveConfigsChangedOperation(in IBinder intentSender, in String packageName);
+    long[] setActiveConfigsChangedOperation(in IPendingIntentRef pendingIntentRef, int callingUid);
 
     /**
      * Removes the active configs changed operation for the specified package name.
      *
      * Requires Manifest.permission.DUMP and Manifest.permission.PACKAGE_USAGE_STATS.
      */
-    void removeActiveConfigsChangedOperation(in String packageName);
+    void removeActiveConfigsChangedOperation(int callingUid);
 
     /**
      * Removes the configuration with the matching config key. No-op if this config key does not
@@ -149,34 +151,31 @@
     void removeConfiguration(in long configKey, in String packageName);
 
     /**
-     * Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
+     * Set the PendingIntentRef to be used when broadcasting subscriber
      * information to the given subscriberId within the given config.
      *
-     * Suppose that the calling uid has added a config with key configKey, and that in this config
+     * Suppose that the calling uid has added a config with key configId, and that in this config
      * it is specified that when a particular anomaly is detected, a broadcast should be sent to
-     * a BroadcastSubscriber with id subscriberId. This function links the given intentSender with
-     * that subscriberId (for that config), so that this intentSender is used to send the broadcast
+     * a BroadcastSubscriber with id subscriberId. This function links the given pendingIntent with
+     * that subscriberId (for that config), so that this pendingIntent is used to send the broadcast
      * when the anomaly is detected.
      *
      * This function can only be called by the owner (uid) of the config. It must be called each
-     * time statsd starts. Later calls overwrite previous calls; only one intentSender is stored.
-     *
-     * intentSender must be convertible into an IntentSender using IntentSender(IBinder)
-     * and cannot be null.
+     * time statsd starts. Later calls overwrite previous calls; only one pendingIntent is stored.
      *
      * Requires Manifest.permission.DUMP.
      */
-    void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender,
-                                in String packageName);
+    void setBroadcastSubscriber(long configId, long subscriberId, in IPendingIntentRef pir,
+                                int callingUid);
 
     /**
-     * Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
+     * Undoes setBroadcastSubscriber() for the (configId, subscriberId) pair.
      * Any broadcasts associated with subscriberId will henceforth not be sent.
-     * No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
+     * No-op if this (configKey, subscriberId) pair was not associated with an PendingIntentRef.
      *
      * Requires Manifest.permission.DUMP.
      */
-    void unsetBroadcastSubscriber(long configKey, long subscriberId, in String packageName);
+    void unsetBroadcastSubscriber(long configId, long subscriberId, int callingUid);
 
     /**
      * Apps can send an atom via this application breadcrumb with the specified label and state for
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 71b52e2..4383b50 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java
@@ -16,11 +16,21 @@
 
 package com.android.server.stats;
 
+import android.app.PendingIntent;
+import android.app.StatsManager;
 import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IPendingIntentRef;
+import android.os.Process;
+import android.os.StatsDimensionsValue;
 import android.util.Slog;
 
 import com.android.server.SystemService;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
 /**
  * @hide
  */
@@ -28,6 +38,13 @@
     private static final String TAG = "StatsCompanion";
     private static final boolean DEBUG = false;
 
+    static void enforceStatsCompanionPermission(Context context) {
+        if (Binder.getCallingPid() == Process.myPid()) {
+            return;
+        }
+        context.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
+    }
+
     /**
      * Lifecycle class for both {@link StatsCompanionService} and {@link StatsManagerService}.
      */
@@ -63,7 +80,97 @@
             super.onBootPhase(phase);
             if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
                 mStatsCompanionService.systemReady();
-                mStatsManagerService.systemReady();
+            }
+        }
+    }
+
+    /**
+     * Wrapper for {@link PendingIntent}. Allows Statsd to send PendingIntents.
+     */
+    public static class PendingIntentRef extends IPendingIntentRef.Stub {
+
+        private static final String TAG = "PendingIntentRef";
+
+        /**
+         * The last report time is provided with each intent registered to
+         * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
+         * statsd is requesting the client to retrieve the same statsd data. The last report time
+         * corresponds to the last_report_elapsed_nanos that will provided in the current
+         * ConfigMetricsReport, and this timestamp also corresponds to the
+         * current_report_elapsed_nanos of the most recently obtained ConfigMetricsReport.
+         */
+        private static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
+        private static final int CODE_DATA_BROADCAST = 1;
+        private static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
+        private static final int CODE_SUBSCRIBER_BROADCAST = 1;
+
+        private final PendingIntent mPendingIntent;
+        private final Context mContext;
+
+        public PendingIntentRef(PendingIntent pendingIntent, Context context) {
+            mPendingIntent = pendingIntent;
+            mContext = context;
+        }
+
+        @Override
+        public void sendDataBroadcast(long lastReportTimeNs) {
+            enforceStatsCompanionPermission(mContext);
+            Intent intent = new Intent();
+            intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
+            try {
+                mPendingIntent.send(mContext, CODE_DATA_BROADCAST, intent, null, null);
+            } catch (PendingIntent.CanceledException e) {
+                Slog.w(TAG, "Unable to send PendingIntent");
+            }
+        }
+
+        @Override
+        public void sendActiveConfigsChangedBroadcast(long[] configIds) {
+            enforceStatsCompanionPermission(mContext);
+            Intent intent = new Intent();
+            intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
+            try {
+                mPendingIntent.send(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
+                if (DEBUG) {
+                    Slog.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");
+            }
+        }
+
+        @Override
+        public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId,
+                long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) {
+            enforceStatsCompanionPermission(mContext);
+            Intent intent =
+                    new Intent()
+                            .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
+                            .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configId)
+                            .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
+                            .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID,
+                                    subscriptionRuleId)
+                            .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+
+            ArrayList<String> cookieList = new ArrayList<>(cookies.length);
+            cookieList.addAll(Arrays.asList(cookies));
+            intent.putStringArrayListExtra(
+                    StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
+
+            if (DEBUG) {
+                Slog.d(TAG,
+                        String.format(
+                                "Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
+                                configUid, configId, subscriptionId, subscriptionRuleId,
+                                Arrays.toString(cookies),
+                                dimensionsValue));
+            }
+            try {
+                mPendingIntent.send(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
+            } catch (PendingIntent.CanceledException e) {
+                Slog.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 7ed51ca..d57afee 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -23,7 +23,6 @@
 import static android.os.storage.VolumeInfo.TYPE_PRIVATE;
 import static android.os.storage.VolumeInfo.TYPE_PUBLIC;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
 import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
 import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
@@ -51,7 +50,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -86,11 +84,9 @@
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
-import android.os.StatsDimensionsValue;
 import android.os.StatsLogEventWrapper;
 import android.os.SynchronousResultReceiver;
 import android.os.SystemClock;
@@ -204,18 +200,6 @@
     private static final int PACKAGE_NAME_FIELD_ID = 4;
     private static final int INSTALLER_FIELD_ID = 5;
 
-    public static final int CODE_DATA_BROADCAST = 1;
-    public static final int CODE_SUBSCRIBER_BROADCAST = 1;
-    public static final int CODE_ACTIVE_CONFIGS_BROADCAST = 1;
-    /**
-     * The last report time is provided with each intent registered to
-     * StatsManager#setFetchReportsOperation. This allows easy de-duping in the receiver if
-     * statsd is requesting the client to retrieve the same statsd data. The last report time
-     * corresponds to the last_report_elapsed_nanos that will provided in the current
-     * ConfigMetricsReport, and this timestamp also corresponds to the
-     * current_report_elapsed_nanos of the most recently obtained ConfigMetricsReport.
-     */
-    public static final String EXTRA_LAST_REPORT_TIME = "android.app.extra.LAST_REPORT_TIME";
     public static final int DEATH_THRESHOLD = 10;
     /**
      * Which native processes to snapshot memory for.
@@ -455,72 +439,6 @@
                 KernelCpuThreadReaderSettingsObserver.getSettingsModifiedReader(mContext);
     }
 
-    @Override
-    public void sendDataBroadcast(IBinder intentSenderBinder, long lastReportTimeNs) {
-        enforceCallingPermission();
-        IntentSender intentSender = new IntentSender(intentSenderBinder);
-        Intent intent = new Intent();
-        intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs);
-        try {
-            intentSender.sendIntent(mContext, CODE_DATA_BROADCAST, intent, null, null);
-        } catch (IntentSender.SendIntentException e) {
-            Slog.w(TAG, "Unable to send using IntentSender");
-        }
-    }
-
-    @Override
-    public void sendActiveConfigsChangedBroadcast(IBinder intentSenderBinder, long[] configIds) {
-        enforceCallingPermission();
-        IntentSender intentSender = new IntentSender(intentSenderBinder);
-        Intent intent = new Intent();
-        intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds);
-        try {
-            intentSender.sendIntent(mContext, CODE_ACTIVE_CONFIGS_BROADCAST, intent, null, null);
-            if (DEBUG) {
-                Slog.d(TAG, "Sent broadcast with config ids " + Arrays.toString(configIds));
-            }
-        } catch (IntentSender.SendIntentException e) {
-            Slog.w(TAG, "Unable to send active configs changed broadcast using IntentSender");
-        }
-    }
-
-    @Override
-    public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
-            long subscriptionId, long subscriptionRuleId, String[] cookies,
-            StatsDimensionsValue dimensionsValue) {
-        enforceCallingPermission();
-        IntentSender intentSender = new IntentSender(intentSenderBinder);
-        Intent intent =
-                new Intent()
-                        .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid)
-                        .putExtra(StatsManager.EXTRA_STATS_CONFIG_KEY, configKey)
-                        .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
-                        .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
-                        .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
-
-        ArrayList<String> cookieList = new ArrayList<>(cookies.length);
-        for (String cookie : cookies) {
-            cookieList.add(cookie);
-        }
-        intent.putStringArrayListExtra(
-                StatsManager.EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES, cookieList);
-
-        if (DEBUG) {
-            Slog.d(TAG,
-                    String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s %s}",
-                            configUid, configKey, subscriptionId, subscriptionRuleId,
-                            Arrays.toString(cookies),
-                            dimensionsValue));
-        }
-        try {
-            intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
-        } catch (IntentSender.SendIntentException e) {
-            Slog.w(TAG,
-                    "Unable to send using IntentSender from uid " + configUid
-                            + "; presumably it had been cancelled.");
-        }
-    }
-
     private final static int[] toIntArray(List<Integer> list) {
         int[] ret = new int[list.size()];
         for (int i = 0; i < ret.length; i++) {
@@ -771,7 +689,7 @@
 
     @Override // Binder call
     public void setAnomalyAlarm(long timestampMs) {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs);
         final long callingToken = Binder.clearCallingIdentity();
         try {
@@ -787,7 +705,7 @@
 
     @Override // Binder call
     public void cancelAnomalyAlarm() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm");
         final long callingToken = Binder.clearCallingIdentity();
         try {
@@ -799,7 +717,7 @@
 
     @Override // Binder call
     public void setAlarmForSubscriberTriggering(long timestampMs) {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG,
                     "Setting periodic alarm in about " + (timestampMs
@@ -818,7 +736,7 @@
 
     @Override // Binder call
     public void cancelAlarmForSubscriberTriggering() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Cancelling periodic alarm");
         }
@@ -832,7 +750,7 @@
 
     @Override // Binder call
     public void setPullingAlarm(long nextPullTimeMs) {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Setting pulling alarm in about "
                     + (nextPullTimeMs - SystemClock.elapsedRealtime()));
@@ -850,7 +768,7 @@
 
     @Override // Binder call
     public void cancelPullingAlarm() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Cancelling pulling alarm");
         }
@@ -1848,7 +1766,7 @@
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         PowerProfile powerProfile = new PowerProfile(mContext);
-        checkNotNull(powerProfile);
+        Objects.requireNonNull(powerProfile);
 
         StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
                 wallClockNanos);
@@ -2456,7 +2374,7 @@
      */
     @Override // Binder call
     public StatsLogEventWrapper[] pullData(int tagId) {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Pulling " + tagId);
         }
@@ -2691,12 +2609,11 @@
 
     @Override // Binder call
     public void statsdReady() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "learned that statsdReady");
         }
         sayHiToStatsd(); // tell statsd that we're ready too and link to it
-        mStatsManagerService.systemReady();
         mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED)
                         .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND),
                 UserHandle.SYSTEM, android.Manifest.permission.DUMP);
@@ -2704,7 +2621,7 @@
 
     @Override
     public void triggerUidSnapshot() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         synchronized (sStatsdLock) {
             final long token = Binder.clearCallingIdentity();
             try {
@@ -2717,13 +2634,6 @@
         }
     }
 
-    private void enforceCallingPermission() {
-        if (Binder.getCallingPid() == Process.myPid()) {
-            return;
-        }
-        mContext.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null);
-    }
-
     @Override
     public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs,
             int[] additiveFields, IPullAtomCallback pullerCallback) {
@@ -2818,6 +2728,7 @@
                                 + "alive.");
                 return;
             }
+            mStatsManagerService.statsdReady(sStatsd);
             if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
             try {
                 sStatsd.statsCompanionReady();
@@ -2929,6 +2840,7 @@
         if (looperStats != null) {
             looperStats.reset();
         }
+        mStatsManagerService.statsdNotReady();
     }
 
     @Override
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 f3bf909..1d03e3b 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java
@@ -16,18 +16,28 @@
 
 package com.android.server.stats;
 
+import static com.android.server.stats.StatsCompanion.PendingIntentRef;
+
+import android.Manifest;
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.Context;
-import android.os.IBinder;
+import android.os.Binder;
 import android.os.IStatsManagerService;
 import android.os.IStatsd;
+import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Map;
+import java.util.Objects;
+
 /**
+ * Service for {@link android.app.StatsManager}.
+ *
  * @hide
  */
 public class StatsManagerService extends IStatsManagerService.Stub {
@@ -35,41 +45,203 @@
     private static final String TAG = "StatsManagerService";
     private static final boolean DEBUG = false;
 
-    @GuardedBy("sStatsdLock")
-    private static IStatsd sStatsd;
-    private static final Object sStatsdLock = new Object();
+    private static final int STATSD_TIMEOUT_MILLIS = 5000;
+
+    private static final String USAGE_STATS_PERMISSION_OPS = "android:get_usage_stats";
+
+    @GuardedBy("mLock")
+    private IStatsd mStatsd;
+    private final Object mLock = new Object();
 
     private StatsCompanionService mStatsCompanionService;
+    private Context mContext;
+
+    @GuardedBy("mLock")
+    private ArrayMap<ConfigKey, PendingIntentRef> mDataFetchPirMap = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private ArrayMap<Integer, PendingIntentRef> mActiveConfigsPirMap =
+            new ArrayMap<>();
+    @GuardedBy("mLock")
+    private ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> mBroadcastSubscriberPirMap =
+            new ArrayMap<>();
 
     public StatsManagerService(Context context) {
         super();
+        mContext = context;
+    }
+
+    private static class ConfigKey {
+        private int mUid;
+        private long mConfigId;
+
+        ConfigKey(int uid, long configId) {
+            mUid = uid;
+            mConfigId = configId;
+        }
+
+        public int getUid() {
+            return mUid;
+        }
+
+        public long getConfigId() {
+            return mConfigId;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mUid, mConfigId);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ConfigKey) {
+                ConfigKey other = (ConfigKey) obj;
+                return this.mUid == other.getUid() && this.mConfigId == other.getConfigId();
+            }
+            return false;
+        }
     }
 
     @Override
-    public void setDataFetchOperation(long configKey, PendingIntent pendingIntent,
+    public void setDataFetchOperation(long configId, PendingIntent pendingIntent,
             String packageName) {
-        // no-op
-        if (DEBUG) {
-            Slog.d(TAG, "setDataFetchOperation");
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+        ConfigKey key = new ConfigKey(callingUid, configId);
+        // We add the PIR to a map so we can reregister if statsd is unavailable.
+        synchronized (mLock) {
+            mDataFetchPirMap.put(key, pir);
+        }
+        try {
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd != null) {
+                statsd.setDataFetchOperation(configId, pir, callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to setDataFetchOperation with statsd");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void removeDataFetchOperation(long configId, String packageName) {
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        ConfigKey key = new ConfigKey(callingUid, configId);
+        synchronized (mLock) {
+            mDataFetchPirMap.remove(key);
+        }
+        try {
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd != null) {
+                statsd.removeDataFetchOperation(configId, callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to removeDataFetchOperation with statsd");
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
     @Override
     public long[] setActiveConfigsChangedOperation(PendingIntent pendingIntent,
             String packageName) {
-        // no-op
-        if (DEBUG) {
-            Slog.d(TAG, "setActiveConfigsChangedOperation");
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+        // We add the PIR to a map so we can reregister if statsd is unavailable.
+        synchronized (mLock) {
+            mActiveConfigsPirMap.put(callingUid, pir);
         }
-        return new long[]{};
+        try {
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd != null) {
+                return statsd.setActiveConfigsChangedOperation(pir, callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to setActiveConfigsChangedOperation with statsd");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        return new long[] {};
     }
 
     @Override
-    public void setBroadcastSubscriber(long configKey, long subscriberId,
+    public void removeActiveConfigsChangedOperation(String packageName) {
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        synchronized (mLock) {
+            mActiveConfigsPirMap.remove(callingUid);
+        }
+        try {
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd != null) {
+                statsd.removeActiveConfigsChangedOperation(callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to removeActiveConfigsChangedOperation with statsd");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void setBroadcastSubscriber(long configId, long subscriberId,
             PendingIntent pendingIntent, String packageName) {
-        //no-op
-        if (DEBUG) {
-            Slog.d(TAG, "setBroadcastSubscriber");
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        PendingIntentRef pir = new PendingIntentRef(pendingIntent, mContext);
+        ConfigKey key = new ConfigKey(callingUid, configId);
+        // We add the PIR to a map so we can reregister if statsd is unavailable.
+        synchronized (mLock) {
+            ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap
+                    .getOrDefault(key, new ArrayMap<>());
+            innerMap.put(subscriberId, pir);
+            mBroadcastSubscriberPirMap.put(key, innerMap);
+        }
+        try {
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd != null) {
+                statsd.setBroadcastSubscriber(
+                        configId, subscriberId, pir, callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to setBroadcastSubscriber with statsd");
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    @Override
+    public void unsetBroadcastSubscriber(long configId, long subscriberId, String packageName) {
+        enforceDumpAndUsageStatsPermission(packageName);
+        int callingUid = Binder.getCallingUid();
+        final long token = Binder.clearCallingIdentity();
+        ConfigKey key = new ConfigKey(callingUid, configId);
+        synchronized (mLock) {
+            ArrayMap<Long, PendingIntentRef> innerMap = mBroadcastSubscriberPirMap
+                    .getOrDefault(key, new ArrayMap<>());
+            innerMap.remove(subscriberId);
+            if (innerMap.isEmpty()) {
+                mBroadcastSubscriberPirMap.remove(key);
+            }
+        }
+        try {
+            IStatsd statsd = getStatsdNonblocking();
+            if (statsd != null) {
+                statsd.unsetBroadcastSubscriber(configId, subscriberId, callingUid);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to unsetBroadcastSubscriber with statsd");
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
@@ -77,31 +249,130 @@
         mStatsCompanionService = statsCompanionService;
     }
 
-    void systemReady() {
-        if (DEBUG) {
-            Slog.d(TAG, "statsdReady");
+    private void enforceDumpAndUsageStatsPermission(String packageName) {
+        int callingUid = Binder.getCallingUid();
+        int callingPid = Binder.getCallingPid();
+
+        if (callingPid == Process.myPid()) {
+            return;
         }
-        setupStatsManagerService();
+        mContext.enforceCallingPermission(Manifest.permission.DUMP, null);
+        mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, null);
+
+        AppOpsManager appOpsManager = (AppOpsManager) mContext
+                .getSystemService(Context.APP_OPS_SERVICE);
+        switch (appOpsManager.noteOp(USAGE_STATS_PERMISSION_OPS,
+                Binder.getCallingUid(), packageName, null, null)) {
+            case AppOpsManager.MODE_ALLOWED:
+            case AppOpsManager.MODE_DEFAULT:
+                break;
+            default:
+                throw new SecurityException(
+                        String.format("UID %d / PID %d lacks app-op %s",
+                                callingUid, callingPid, USAGE_STATS_PERMISSION_OPS)
+                );
+        }
     }
 
-    private void setupStatsManagerService() {
-        synchronized (sStatsdLock) {
-            if (sStatsd != null) {
-                if (DEBUG) {
-                    Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
-                            new IllegalStateException(
-                                    "sStatsd is not null when being fetched"));
+    /**
+     * Clients should call this if blocking until statsd to be ready is desired
+     *
+     * @return IStatsd object if statsd becomes ready within the timeout, null otherwise.
+     */
+    private IStatsd waitForStatsd() {
+        synchronized (mLock) {
+            if (mStatsd == null) {
+                try {
+                    mLock.wait(STATSD_TIMEOUT_MILLIS);
+                } catch (InterruptedException e) {
+                    Slog.e(TAG, "wait for statsd interrupted");
                 }
-                return;
             }
-            sStatsd = IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
-            // Assume statsd is ready since this is called form statscompanion, link to statsd.
+            return mStatsd;
+        }
+    }
+
+    /**
+     * Clients should call this to receive a reference to statsd.
+     *
+     * @return IStatsd object if statsd is ready, null otherwise.
+     */
+    private IStatsd getStatsdNonblocking() {
+        synchronized (mLock) {
+            return mStatsd;
+        }
+    }
+
+    /**
+     * Called from {@link StatsCompanionService}.
+     *
+     * Tells StatsManagerService that Statsd is ready and updates
+     * Statsd with the contents of our local cache.
+     */
+    void statsdReady(IStatsd statsd) {
+        synchronized (mLock) {
+            mStatsd = statsd;
+            mLock.notify();
+        }
+        sayHiToStatsd(statsd);
+    }
+
+    /**
+     * Called from {@link StatsCompanionService}.
+     *
+     * Tells StatsManagerService that Statsd is no longer ready
+     * and we should no longer make binder calls with statsd.
+     */
+    void statsdNotReady() {
+        synchronized (mLock) {
+            mStatsd = null;
+        }
+    }
+
+    private void sayHiToStatsd(IStatsd statsd) {
+        if (statsd == null) {
+            return;
+        }
+        // Since we do not want to make an IPC with the a lock held, we first create local deep
+        // copies of the data with the lock held before iterating through the maps.
+        ArrayMap<ConfigKey, PendingIntentRef> dataFetchCopy;
+        ArrayMap<Integer, PendingIntentRef> activeConfigsChangedCopy;
+        ArrayMap<ConfigKey, ArrayMap<Long, PendingIntentRef>> broadcastSubscriberCopy;
+        synchronized (mLock) {
+            dataFetchCopy = new ArrayMap<>(mDataFetchPirMap);
+            activeConfigsChangedCopy = new ArrayMap<>(mActiveConfigsPirMap);
+            broadcastSubscriberCopy = new ArrayMap<>();
+            for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
+                    : mBroadcastSubscriberPirMap.entrySet()) {
+                broadcastSubscriberCopy.put(entry.getKey(), new ArrayMap<>(entry.getValue()));
+            }
+        }
+        for (Map.Entry<ConfigKey, PendingIntentRef> entry : dataFetchCopy.entrySet()) {
+            ConfigKey key = entry.getKey();
             try {
-                sStatsd.asBinder().linkToDeath((IBinder.DeathRecipient) () -> {
-                    sStatsd = null;
-                }, 0);
+                statsd.setDataFetchOperation(key.getConfigId(), entry.getValue(), key.getUid());
             } catch (RemoteException e) {
-                Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
+                Slog.e(TAG, "Failed to setDataFetchOperation from pirMap");
+            }
+        }
+        for (Map.Entry<Integer, PendingIntentRef> entry
+                : activeConfigsChangedCopy.entrySet()) {
+            try {
+                statsd.setActiveConfigsChangedOperation(entry.getValue(), entry.getKey());
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to setActiveConfigsChangedOperation from pirMap");
+            }
+        }
+        for (Map.Entry<ConfigKey, ArrayMap<Long, PendingIntentRef>> entry
+                : broadcastSubscriberCopy.entrySet()) {
+            for (Map.Entry<Long, PendingIntentRef> subscriberEntry : entry.getValue().entrySet()) {
+                ConfigKey configKey = entry.getKey();
+                try {
+                    statsd.setBroadcastSubscriber(configKey.getConfigId(), subscriberEntry.getKey(),
+                            subscriberEntry.getValue(), configKey.getUid());
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to setBroadcastSubscriber from pirMap");
+                }
             }
         }
     }
diff --git a/api/current.txt b/api/current.txt
index 2223375..51641f2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -87,6 +87,7 @@
     field public static final String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
     field public static final String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
     field public static final String INSTANT_APP_FOREGROUND_SERVICE = "android.permission.INSTANT_APP_FOREGROUND_SERVICE";
+    field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
     field public static final String INTERNET = "android.permission.INTERNET";
     field public static final String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
     field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
@@ -478,6 +479,7 @@
     field public static final int countDown = 16844059; // 0x101051b
     field public static final int country = 16843962; // 0x10104ba
     field public static final int cropToPadding = 16843043; // 0x1010123
+    field public static final int crossProfile = 16844303; // 0x101060f
     field public static final int cursorVisible = 16843090; // 0x1010152
     field public static final int customNavigationLayout = 16843474; // 0x10102d2
     field public static final int customTokens = 16843579; // 0x101033b
@@ -611,6 +613,7 @@
     field public static final int fastScrollTextColor = 16843609; // 0x1010359
     field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336
     field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339
+    field public static final int featureId = 16844301; // 0x101060d
     field public static final int fillAfter = 16843197; // 0x10101bd
     field public static final int fillAlpha = 16843980; // 0x10104cc
     field public static final int fillBefore = 16843196; // 0x10101bc
@@ -1332,6 +1335,7 @@
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsAssist = 16844016; // 0x10104f0
+    field public static final int supportsInlineSuggestions = 16844302; // 0x101060e
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
     field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
@@ -4499,7 +4503,6 @@
     method public int describeContents();
     method @Nullable public String getFeatureId();
     method @NonNull public String getMessage();
-    method @Nullable public String getNotingPackageName();
     method @IntRange(from=0) public int getNotingUid();
     method @NonNull public String getOp();
     method @IntRange(from=0) public long getTime();
@@ -6787,6 +6790,7 @@
     method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName);
     method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName);
     method @Nullable public java.util.List<java.lang.String> getPermittedInputMethods(@NonNull android.content.ComponentName);
+    method @NonNull public java.util.List<java.lang.String> getProtectedPackages(@NonNull android.content.ComponentName);
     method public long getRequiredStrongAuthTimeout(@Nullable android.content.ComponentName);
     method public boolean getScreenCaptureDisabled(@Nullable android.content.ComponentName);
     method public java.util.List<android.os.UserHandle> getSecondaryUsers(@NonNull android.content.ComponentName);
@@ -6908,6 +6912,7 @@
     method public boolean setPermittedInputMethods(@NonNull android.content.ComponentName, java.util.List<java.lang.String>);
     method public void setProfileEnabled(@NonNull android.content.ComponentName);
     method public void setProfileName(@NonNull android.content.ComponentName, String);
+    method public void setProtectedPackages(@NonNull android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
     method public void setRecommendedGlobalProxy(@NonNull android.content.ComponentName, @Nullable android.net.ProxyInfo);
     method public void setRequiredStrongAuthTimeout(@NonNull android.content.ComponentName, long);
     method public boolean setResetPasswordToken(android.content.ComponentName, byte[]);
@@ -7125,6 +7130,7 @@
     field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
     field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
     field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
+    field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
     field public static final int TAG_CERT_AUTHORITY_INSTALLED = 210029; // 0x3346d
     field public static final int TAG_CERT_AUTHORITY_REMOVED = 210030; // 0x3346e
     field public static final int TAG_CERT_VALIDATION_FAILURE = 210033; // 0x33471
@@ -28675,6 +28681,7 @@
     method public int getVideoHeight();
     method public float getVideoPixelAspectRatio();
     method public int getVideoWidth();
+    method public boolean isAudioDescription();
     method public boolean isEncrypted();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TvTrackInfo> CREATOR;
@@ -28687,6 +28694,7 @@
     ctor public TvTrackInfo.Builder(int, @NonNull String);
     method public android.media.tv.TvTrackInfo build();
     method public android.media.tv.TvTrackInfo.Builder setAudioChannelCount(int);
+    method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioDescription(boolean);
     method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
     method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence);
     method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean);
@@ -29236,6 +29244,7 @@
     method public boolean addRoute(@NonNull android.net.RouteInfo);
     method public void clear();
     method public int describeContents();
+    method @Nullable public java.net.Inet4Address getDhcpServerAddress();
     method @NonNull public java.util.List<java.net.InetAddress> getDnsServers();
     method @Nullable public String getDomains();
     method @Nullable public android.net.ProxyInfo getHttpProxy();
@@ -29247,6 +29256,7 @@
     method @NonNull public java.util.List<android.net.RouteInfo> getRoutes();
     method public boolean isPrivateDnsActive();
     method public boolean isWakeOnLanSupported();
+    method public void setDhcpServerAddress(@Nullable java.net.Inet4Address);
     method public void setDnsServers(@NonNull java.util.Collection<java.net.InetAddress>);
     method public void setDomains(@Nullable String);
     method public void setHttpProxy(@Nullable android.net.ProxyInfo);
@@ -29471,6 +29481,7 @@
     method public android.net.NetworkRequest.Builder addCapability(int);
     method public android.net.NetworkRequest.Builder addTransportType(int);
     method public android.net.NetworkRequest build();
+    method @NonNull public android.net.NetworkRequest.Builder clearCapabilities();
     method public android.net.NetworkRequest.Builder removeCapability(int);
     method public android.net.NetworkRequest.Builder removeTransportType(int);
     method public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
@@ -30235,6 +30246,7 @@
 
   @Deprecated public class WifiConfiguration implements android.os.Parcelable {
     ctor @Deprecated public WifiConfiguration();
+    ctor @Deprecated public WifiConfiguration(@NonNull android.net.wifi.WifiConfiguration);
     method public int describeContents();
     method @Deprecated public android.net.ProxyInfo getHttpProxy();
     method @Deprecated @NonNull public String getKey();
@@ -30345,6 +30357,7 @@
     method public String getPlmn();
     method public String getRealm();
     method @Deprecated public String getSubjectMatch();
+    method public boolean isAuthenticationSimBased();
     method public void setAltSubjectMatch(String);
     method public void setAnonymousIdentity(String);
     method public void setCaCertificate(@Nullable java.security.cert.X509Certificate);
@@ -36089,6 +36102,37 @@
     method public int getUserOperationResult();
   }
 
+  public final class VibrationAttributes implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getFlags();
+    method public int getUsage();
+    method public int getUsageClass();
+    method public boolean isFlagSet(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationAttributes> CREATOR;
+    field public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 1; // 0x1
+    field public static final int USAGE_ALARM = 17; // 0x11
+    field public static final int USAGE_CLASS_ALARM = 1; // 0x1
+    field public static final int USAGE_CLASS_FEEDBACK = 2; // 0x2
+    field public static final int USAGE_CLASS_MASK = 15; // 0xf
+    field public static final int USAGE_CLASS_UNKNOWN = 0; // 0x0
+    field public static final int USAGE_COMMUNICATION_REQUEST = 65; // 0x41
+    field public static final int USAGE_HARDWARE_FEEDBACK = 50; // 0x32
+    field public static final int USAGE_NOTIFICATION = 49; // 0x31
+    field public static final int USAGE_PHYSICAL_EMULATION = 34; // 0x22
+    field public static final int USAGE_RINGTONE = 33; // 0x21
+    field public static final int USAGE_TOUCH = 18; // 0x12
+    field public static final int USAGE_UNKNOWN = 0; // 0x0
+  }
+
+  public static final class VibrationAttributes.Builder {
+    ctor public VibrationAttributes.Builder();
+    ctor public VibrationAttributes.Builder(@Nullable android.os.VibrationAttributes);
+    method @NonNull public android.os.VibrationAttributes build();
+    method @NonNull public android.os.VibrationAttributes.Builder replaceFlags(int);
+    method @NonNull public android.os.VibrationAttributes.Builder setUsage(int);
+  }
+
   public abstract class VibrationEffect implements android.os.Parcelable {
     method public static android.os.VibrationEffect createOneShot(long, int);
     method @NonNull public static android.os.VibrationEffect createPredefined(int);
@@ -41782,6 +41826,7 @@
   }
 
   public static final class Dataset.Builder {
+    ctor public Dataset.Builder(@NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
     ctor public Dataset.Builder(@NonNull android.widget.RemoteViews);
     ctor public Dataset.Builder();
     method @NonNull public android.service.autofill.Dataset build();
@@ -41791,6 +41836,8 @@
     method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews);
     method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern);
     method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews);
+    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Dataset.Builder setValue(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.widget.RemoteViews, @NonNull android.service.autofill.InlinePresentation);
   }
 
   public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -41879,7 +41926,6 @@
   public static final class FillResponse.Builder {
     ctor public FillResponse.Builder();
     method @NonNull public android.service.autofill.FillResponse.Builder addDataset(@Nullable android.service.autofill.Dataset);
-    method @NonNull public android.service.autofill.FillResponse.Builder addInlineSuggestionSlice(@NonNull android.app.slice.Slice);
     method @NonNull public android.service.autofill.FillResponse build();
     method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
     method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
@@ -41908,6 +41954,15 @@
     method public android.service.autofill.ImageTransformation build();
   }
 
+  public final class InlinePresentation implements android.os.Parcelable {
+    ctor public InlinePresentation(@NonNull android.app.slice.Slice, @NonNull android.view.inline.InlinePresentationSpec);
+    method public int describeContents();
+    method @NonNull public android.view.inline.InlinePresentationSpec getInlinePresentationSpec();
+    method @NonNull public android.app.slice.Slice getSlice();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.InlinePresentation> CREATOR;
+  }
+
   public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public LuhnChecksumValidator(@NonNull android.view.autofill.AutofillId...);
     method public int describeContents();
@@ -43123,6 +43178,7 @@
     method public static void execve(String, String[], String[]) throws android.system.ErrnoException;
     method public static void fchmod(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static void fchown(java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
+    method public static int fcntlInt(@NonNull java.io.FileDescriptor, int, int) throws android.system.ErrnoException;
     method public static void fdatasync(java.io.FileDescriptor) throws android.system.ErrnoException;
     method public static android.system.StructStat fstat(java.io.FileDescriptor) throws android.system.ErrnoException;
     method public static android.system.StructStatVfs fstatvfs(java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -45543,6 +45599,7 @@
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void onMessageWaitingIndicatorChanged(boolean);
     method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
+    method public void onRegistrationFailed(@NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
     method public void onServiceStateChanged(android.telephony.ServiceState);
     method @Deprecated public void onSignalStrengthChanged(int);
     method public void onSignalStrengthsChanged(android.telephony.SignalStrength);
@@ -45560,6 +45617,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.MODIFY_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 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
@@ -45635,6 +45693,7 @@
     method public int getLevel();
     method @Deprecated public boolean isGsm();
     method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrength> CREATOR;
     field public static final int INVALID = 2147483647; // 0x7fffffff
   }
 
@@ -45643,7 +45702,7 @@
     method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
     method public java.util.ArrayList<java.lang.String> divideMessage(String);
     method public void downloadMultimediaMessage(android.content.Context, String, android.net.Uri, android.os.Bundle, android.app.PendingIntent);
-    method public android.os.Bundle getCarrierConfigValues();
+    method @Nullable public android.os.Bundle getCarrierConfigValues();
     method public static android.telephony.SmsManager getDefault();
     method public static int getDefaultSmsSubscriptionId();
     method public static android.telephony.SmsManager getSmsManagerForSubscriptionId(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 405aab3..41ded16 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -90,7 +90,6 @@
     field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
     field public static final String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
     field public static final String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
-    field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
     field public static final String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
     field public static final String INTERACT_ACROSS_USERS_FULL = "android.permission.INTERACT_ACROSS_USERS_FULL";
     field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
@@ -111,6 +110,7 @@
     field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
     field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS";
+    field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS";
     field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS";
     field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
     field public static final String MANAGE_SENSOR_PRIVACY = "android.permission.MANAGE_SENSOR_PRIVACY";
@@ -127,6 +127,7 @@
     field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
     field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
     field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
+    field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
     field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
     field public static final String NETWORK_FACTORY = "android.permission.NETWORK_FACTORY";
     field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
@@ -362,6 +363,7 @@
     field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts";
     field public static final String OPSTR_GPS = "android:gps";
     field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
+    field public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
     field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
     field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
     field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
@@ -411,6 +413,16 @@
     field public static final int UID_STATE_TOP = 200; // 0xc8
   }
 
+  public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getFeatureId();
+    method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
+    method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getOpCount();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+  }
+
   public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
     method public int describeContents();
     method public long getAccessCount(int, int, int);
@@ -444,6 +456,7 @@
   public static final class AppOpsManager.HistoricalOpsRequest.Builder {
     ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -452,6 +465,9 @@
 
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
+    method @IntRange(from=0) public int getFeatureCount();
+    method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
+    method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
     method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
     method @IntRange(from=0) public int getOpCount();
@@ -1266,6 +1282,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @Nullable public String getDefaultSmsPackage(int);
     method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
@@ -1515,6 +1532,14 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
   }
 
+  public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
+    method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
+  }
+
   public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
     method protected void finalize();
     method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
@@ -1532,6 +1557,7 @@
 
   public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
     method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
   }
 
@@ -1657,6 +1683,7 @@
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
     field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
     field public static final String NETD_SERVICE = "netd";
+    field public static final String NETWORK_POLICY_SERVICE = "netpolicy";
     field public static final String NETWORK_SCORE_SERVICE = "network_score";
     field public static final String OEM_LOCK_SERVICE = "oem_lock";
     field public static final String PERMISSION_SERVICE = "permission";
@@ -1695,6 +1722,7 @@
     field public static final String ACTION_INSTALL_INSTANT_APP_PACKAGE = "android.intent.action.INSTALL_INSTANT_APP_PACKAGE";
     field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
     field public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+    field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
     field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
     field public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
     field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
@@ -2113,6 +2141,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_REBOOT_ESCROW = "android.hardware.reboot_escrow";
     field public static final String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
     field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000
     field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20
@@ -4099,6 +4128,10 @@
     method public android.media.AudioRecord.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
   }
 
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
+    method public int getClientUid();
+  }
+
   public class HwAudioSource {
     method public boolean isPlaying();
     method public void start();
@@ -4133,6 +4166,14 @@
 
 }
 
+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);
+  }
+
+}
+
 package android.media.audiopolicy {
 
   public class AudioMix {
@@ -4325,6 +4366,15 @@
 
 package android.media.tv {
 
+  public final class DvbDeviceInfo implements android.os.Parcelable {
+    ctor public DvbDeviceInfo(int, int);
+    method public int describeContents();
+    method public int getAdapterId();
+    method public int getDeviceId();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
+  }
+
   public final class TvContentRatingSystemInfo implements android.os.Parcelable {
     method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo);
     method public int describeContents();
@@ -4439,12 +4489,14 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig);
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String);
+    method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList();
     method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList();
     method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList();
     method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean isSingleSessionActive();
     method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyPreviewProgramAddedToWatchNext(String, long, long);
     method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyPreviewProgramBrowsableDisabled(String, long);
     method @RequiresPermission(android.Manifest.permission.NOTIFY_TV_INPUTS) public void notifyWatchNextProgramBrowsableDisabled(String, long);
+    method @Nullable @RequiresPermission("android.permission.DVB_DEVICE") public android.os.ParcelFileDescriptor openDvbDevice(@NonNull android.media.tv.DvbDeviceInfo, int);
     method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public void releaseTvInputHardware(int, android.media.tv.TvInputManager.Hardware);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void removeBlockedRating(@NonNull android.media.tv.TvContentRating);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void setParentalControlsEnabled(boolean);
@@ -4533,6 +4585,36 @@
   public class Tuner.Descrambler {
   }
 
+  public class Tuner.Filter {
+  }
+
+  public static interface Tuner.FilterCallback {
+    method public void onFilterEvent(@NonNull android.media.tv.tuner.Tuner.Filter, @NonNull android.media.tv.tuner.filter.FilterEvent[]);
+    method public void onFilterStatusChanged(@NonNull android.media.tv.tuner.Tuner.Filter, int);
+  }
+
+  public final class TunerConstants {
+    field public static final int FILTER_STATUS_DATA_READY = 1; // 0x1
+    field public static final int FILTER_STATUS_HIGH_WATER = 4; // 0x4
+    field public static final int FILTER_STATUS_LOW_WATER = 2; // 0x2
+    field public static final int FILTER_STATUS_OVERFLOW = 8; // 0x8
+  }
+
+}
+
+package android.media.tv.tuner.filter {
+
+  public abstract class FilterEvent {
+    ctor public FilterEvent();
+  }
+
+  public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
+    method public int getDataLength();
+    method public int getSectionNumber();
+    method public int getTableId();
+    method public int getVersion();
+  }
+
 }
 
 package android.metrics {
@@ -4596,7 +4678,7 @@
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
     method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
     method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -4741,6 +4823,7 @@
   public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     ctor public MatchAllNetworkSpecifier();
     method public int describeContents();
+    method public boolean satisfiedBy(android.net.NetworkSpecifier);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.MatchAllNetworkSpecifier> CREATOR;
   }
@@ -4804,6 +4887,12 @@
     method public void updateScores(@NonNull java.util.List<android.net.ScoredNetwork>);
   }
 
+  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);
+  }
+
   public class NetworkStack {
     field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
   }
@@ -4874,6 +4963,7 @@
   public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
     ctor public StringNetworkSpecifier(@NonNull String);
     method public int describeContents();
+    method public boolean satisfiedBy(android.net.NetworkSpecifier);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR;
     field @NonNull public final String specifier;
@@ -5621,8 +5711,32 @@
   }
 
   public class ScanResult implements android.os.Parcelable {
+    field public static final int CIPHER_CCMP = 3; // 0x3
+    field public static final int CIPHER_GCMP_256 = 4; // 0x4
+    field public static final int CIPHER_NONE = 0; // 0x0
+    field public static final int CIPHER_NO_GROUP_ADDRESSED = 1; // 0x1
+    field public static final int CIPHER_SMS4 = 5; // 0x5
+    field public static final int CIPHER_TKIP = 2; // 0x2
+    field public static final int KEY_MGMT_EAP = 2; // 0x2
+    field public static final int KEY_MGMT_EAP_SHA256 = 6; // 0x6
+    field public static final int KEY_MGMT_EAP_SUITE_B_192 = 10; // 0xa
+    field public static final int KEY_MGMT_FT_EAP = 4; // 0x4
+    field public static final int KEY_MGMT_FT_PSK = 3; // 0x3
+    field public static final int KEY_MGMT_FT_SAE = 11; // 0xb
+    field public static final int KEY_MGMT_NONE = 0; // 0x0
+    field public static final int KEY_MGMT_OSEN = 7; // 0x7
+    field public static final int KEY_MGMT_OWE = 9; // 0x9
+    field public static final int KEY_MGMT_OWE_TRANSITION = 12; // 0xc
+    field public static final int KEY_MGMT_PSK = 1; // 0x1
+    field public static final int KEY_MGMT_PSK_SHA256 = 5; // 0x5
+    field public static final int KEY_MGMT_SAE = 8; // 0x8
     field public static final int KEY_MGMT_WAPI_CERT = 14; // 0xe
     field public static final int KEY_MGMT_WAPI_PSK = 13; // 0xd
+    field public static final int PROTOCOL_NONE = 0; // 0x0
+    field public static final int PROTOCOL_OSEN = 3; // 0x3
+    field public static final int PROTOCOL_RSN = 2; // 0x2
+    field public static final int PROTOCOL_WAPI = 4; // 0x4
+    field public static final int PROTOCOL_WPA = 1; // 0x1
   }
 
   public final class SoftApCapability implements android.os.Parcelable {
@@ -5633,6 +5747,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApCapability> CREATOR;
     field public static final int SOFTAP_FEATURE_ACS_OFFLOAD = 1; // 0x1
     field public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 2; // 0x2
+    field public static final int SOFTAP_FEATURE_WPA3_SAE = 4; // 0x4
   }
 
   public final class SoftApConfiguration implements android.os.Parcelable {
@@ -5641,6 +5756,7 @@
     method @Nullable public android.net.MacAddress getBssid();
     method public int getChannel();
     method public int getMaxNumberOfClients();
+    method @Nullable public String getPassphrase();
     method public int getSecurityType();
     method @Nullable public String getSsid();
     method @Nullable public String getWpa2Passphrase();
@@ -5653,6 +5769,8 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApConfiguration> CREATOR;
     field public static final int SECURITY_TYPE_OPEN = 0; // 0x0
     field public static final int SECURITY_TYPE_WPA2_PSK = 1; // 0x1
+    field public static final int SECURITY_TYPE_WPA3_SAE = 3; // 0x3
+    field public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2; // 0x2
   }
 
   public static final class SoftApConfiguration.Builder {
@@ -5664,6 +5782,7 @@
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setChannel(int, int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setHiddenSsid(boolean);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setMaxNumberOfClients(int);
+    method @NonNull public android.net.wifi.SoftApConfiguration.Builder setPassphrase(@Nullable String, int);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setSsid(@Nullable String);
     method @NonNull public android.net.wifi.SoftApConfiguration.Builder setWpa2Passphrase(@Nullable String);
   }
@@ -5693,6 +5812,7 @@
   @Deprecated public class WifiConfiguration implements android.os.Parcelable {
     method @Deprecated public int getAuthType();
     method @Deprecated @NonNull public android.net.IpConfiguration.IpAssignment getIpAssignment();
+    method @Deprecated @NonNull public android.net.IpConfiguration getIpConfiguration();
     method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus getNetworkSelectionStatus();
     method @Deprecated @NonNull public String getPrintableSsid();
     method @Deprecated @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
@@ -5961,6 +6081,10 @@
     field public int numUsage;
   }
 
+  public final class WifiNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    method public boolean satisfiedBy(android.net.NetworkSpecifier);
+  }
+
   public static final class WifiNetworkSuggestion.Builder {
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_CARRIER_PROVISIONING) public android.net.wifi.WifiNetworkSuggestion.Builder setCarrierId(int);
   }
@@ -6147,6 +6271,10 @@
     method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]);
   }
 
+  public final class WifiAwareNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
+    method public boolean satisfiedBy(android.net.NetworkSpecifier);
+  }
+
   public class WifiAwareSession implements java.lang.AutoCloseable {
     method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]);
   }
@@ -6361,6 +6489,7 @@
     method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
     method public boolean initialize(@NonNull Runnable);
+    method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]);
     method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback);
     method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback);
     method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback);
@@ -6381,6 +6510,14 @@
     field public static final int SEND_MGMT_FRAME_ERROR_UNKNOWN = 1; // 0x1
   }
 
+  public static class WifiCondManager.OemSecurityType {
+    ctor public WifiCondManager.OemSecurityType(int, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, int);
+    field public final int groupCipher;
+    field @NonNull public final java.util.List<java.lang.Integer> keyManagement;
+    field @NonNull public final java.util.List<java.lang.Integer> pairwiseCipher;
+    field public final int protocol;
+  }
+
   public static interface WifiCondManager.PnoScanRequestCallback {
     method public void onPnoRequestFailed();
     method public void onPnoRequestSucceeded();
@@ -6852,9 +6989,12 @@
 
   public class RecoverySystem {
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void cancelScheduledUpdate(android.content.Context) throws java.io.IOException;
+    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean clearPrepareForUnattendedUpdate(@NonNull android.content.Context) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void installPackage(android.content.Context, java.io.File, boolean) throws java.io.IOException;
+    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void prepareForUnattendedUpdate(@NonNull android.content.Context, @NonNull String, @Nullable android.content.IntentSender) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
+    method @RequiresPermission(android.Manifest.permission.RECOVERY) public static boolean rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
     method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
     method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
@@ -7242,8 +7382,8 @@
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
-    method @RequiresPermission(allOf={android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void startOneTimePermissionSession(@NonNull String, long, int, int);
-    method @RequiresPermission(allOf={android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void stopOneTimePermissionSession(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
   }
 
   public static final class PermissionManager.SplitPermissionInfo {
@@ -7707,7 +7847,9 @@
   public static final class Telephony.Carriers implements android.provider.BaseColumns {
     field public static final String APN_SET_ID = "apn_set_id";
     field public static final int CARRIER_EDITED = 4; // 0x4
+    field @NonNull public static final android.net.Uri DPC_URI;
     field public static final String EDITED_STATUS = "edited";
+    field public static final int INVALID_APN_ID = -1; // 0xffffffff
     field public static final String MAX_CONNECTIONS = "max_conns";
     field public static final String MODEM_PERSIST = "modem_cognitive";
     field public static final String MTU = "mtu";
@@ -7722,6 +7864,9 @@
   }
 
   public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns {
+    field @NonNull public static final String AUTHORITY_LEGACY = "cellbroadcast-legacy";
+    field @NonNull public static final android.net.Uri AUTHORITY_LEGACY_URI;
+    field @NonNull public static final String CALL_METHOD_GET_PREFERENCE = "get_preference";
     field public static final String CID = "cid";
     field public static final String CMAS_CATEGORY = "cmas_category";
     field public static final String CMAS_CERTAINTY = "cmas_certainty";
@@ -7749,7 +7894,22 @@
     field public static final String SERIAL_NUMBER = "serial_number";
     field public static final String SERVICE_CATEGORY = "service_category";
     field public static final String SLOT_INDEX = "slot_index";
-    field public static final String SUB_ID = "sub_id";
+    field public static final String SUBSCRIPTION_ID = "sub_id";
+  }
+
+  public static final class Telephony.CellBroadcasts.Preference {
+    field @NonNull public static final String ENABLE_ALERT_VIBRATION_PREF = "enable_alert_vibrate";
+    field @NonNull public static final String ENABLE_AREA_UPDATE_INFO_PREF = "enable_area_update_info_alerts";
+    field @NonNull public static final String ENABLE_CMAS_AMBER_PREF = "enable_cmas_amber_alerts";
+    field @NonNull public static final String ENABLE_CMAS_EXTREME_THREAT_PREF = "enable_cmas_extreme_threat_alerts";
+    field @NonNull public static final String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF = "receive_cmas_in_second_language";
+    field @NonNull public static final String ENABLE_CMAS_PRESIDENTIAL_PREF = "enable_cmas_presidential_alerts";
+    field @NonNull public static final String ENABLE_CMAS_SEVERE_THREAT_PREF = "enable_cmas_severe_threat_alerts";
+    field @NonNull public static final String ENABLE_EMERGENCY_PERF = "enable_emergency_alerts";
+    field @NonNull public static final String ENABLE_FULL_VOLUME_PREF = "use_full_volume";
+    field @NonNull public static final String ENABLE_PUBLIC_SAFETY_PREF = "enable_public_safety_messages";
+    field @NonNull public static final String ENABLE_STATE_LOCAL_TEST_PREF = "enable_state_local_test_alerts";
+    field @NonNull public static final String ENABLE_TEST_ALERT_PREF = "enable_test_alerts";
   }
 
   public static final class Telephony.SimInfo {
@@ -7980,6 +8140,11 @@
     field public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm";
   }
 
+  public static final class Dataset.Builder {
+    ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation);
+    method @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation);
+  }
+
 }
 
 package android.service.autofill.augmented {
@@ -9438,6 +9603,7 @@
     field public static final int IMS_ACCESS_BLOCKED = 60; // 0x3c
     field public static final int IMS_MERGED_SUCCESSFULLY = 45; // 0x2d
     field public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71; // 0x47
+    field public static final int INCOMING_AUTO_REJECTED = 81; // 0x51
     field public static final int INCOMING_MISSED = 1; // 0x1
     field public static final int INCOMING_REJECTED = 16; // 0x10
     field public static final int INVALID_CREDENTIALS = 10; // 0xa
@@ -9539,7 +9705,9 @@
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR;
     field public static final int DOMAIN_CS = 1; // 0x1
+    field public static final int DOMAIN_CS_PS = 3; // 0x3
     field public static final int DOMAIN_PS = 2; // 0x2
+    field public static final int DOMAIN_UNKNOWN = 0; // 0x0
     field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
     field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
     field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
@@ -9763,6 +9931,8 @@
   }
 
   public class ServiceState implements android.os.Parcelable {
+    method @NonNull public android.telephony.ServiceState createLocationInfoSanitizedCopy(boolean);
+    method public void fillInNotifierBundle(@NonNull android.os.Bundle);
     method public int getDataRegistrationState();
     method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
     method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoList();
@@ -9771,12 +9941,17 @@
     method public int getNrFrequencyRange();
     method @Nullable public String getOperatorAlphaLongRaw();
     method @Nullable public String getOperatorAlphaShortRaw();
+    method @NonNull public static android.telephony.ServiceState newFromBundle(@NonNull android.os.Bundle);
     field public static final int ROAMING_TYPE_DOMESTIC = 2; // 0x2
     field public static final int ROAMING_TYPE_INTERNATIONAL = 3; // 0x3
     field public static final int ROAMING_TYPE_NOT_ROAMING = 0; // 0x0
     field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
   }
 
+  public class SignalStrength implements android.os.Parcelable {
+    ctor public SignalStrength(@NonNull android.telephony.SignalStrength);
+  }
+
   public final class SmsCbCmasInfo implements android.os.Parcelable {
     ctor public SmsCbCmasInfo(int, int, int, int, int, int);
     method public int describeContents();
@@ -9919,6 +10094,9 @@
   public class SubscriptionManager {
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
     method public boolean canManageSubscription(@Nullable android.telephony.SubscriptionInfo, @Nullable String);
+    method @NonNull public int[] getActiveAndHiddenSubscriptionIdList();
+    method @NonNull public int[] getActiveSubscriptionIdList();
+    method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
     method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
     method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
@@ -9979,6 +10157,7 @@
   }
 
   public class TelephonyManager {
+    method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String);
     method public int checkCarrierPrivilegesForPackage(String);
     method public int checkCarrierPrivilegesForPackageAnyPhone(String);
@@ -10007,6 +10186,7 @@
     method @Deprecated public boolean getDataEnabled(int);
     method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
     method @NonNull public static String getDefaultSimCountryIso();
+    method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDeviceSoftwareVersion(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
     method public int getEmergencyNumberDbVersion();
@@ -10044,15 +10224,17 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isManualNetworkSelectionAllowed();
     method public boolean isModemEnabledForSlot(int);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isOpportunisticNetworkEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isPotentialEmergencyNumber(@NonNull String);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTetheringApnRequired();
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isTetheringApnRequired();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
+    method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method public boolean needsOtaServiceProvisioning();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyOtaEmergencyNumberDbInstalled();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
@@ -10065,6 +10247,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
     method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings();
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAlwaysAllowMmsData(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
@@ -10072,6 +10255,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPolicyDataEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean);
@@ -10090,10 +10274,13 @@
     method public void updateServiceLocation();
     method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String);
     field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED";
+    field public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+    field public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
     field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
     field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
     field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
     field public static final String ACTION_NETWORK_SET_TIME = "android.telephony.action.NETWORK_SET_TIME";
+    field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
     field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
     field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
     field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
@@ -10155,6 +10342,7 @@
     method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
     method public void notifyCarrierNetworkChange(boolean);
+    method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
     method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
     method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
   }
@@ -10573,6 +10761,7 @@
     field public static final int DIALSTRING_USSD = 2; // 0x2
     field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+    field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
     field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
     field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
     field public static final String EXTRA_CNA = "cna";
diff --git a/api/test-current.txt b/api/test-current.txt
index e7153e6..9967942 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -244,6 +244,16 @@
     field public static final int UID_STATE_TOP = 200; // 0xc8
   }
 
+  public static final class AppOpsManager.HistoricalFeatureOps implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getFeatureId();
+    method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
+    method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
+    method @IntRange(from=0) public int getOpCount();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalFeatureOps> CREATOR;
+  }
+
   public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable {
     method public int describeContents();
     method public long getAccessCount(int, int, int);
@@ -268,9 +278,9 @@
     method @IntRange(from=0) public int getUidCount();
     method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int);
     method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(@IntRange(from=0) int);
-    method public void increaseAccessCount(int, int, @NonNull String, int, int, long);
-    method public void increaseAccessDuration(int, int, @NonNull String, int, int, long);
-    method public void increaseRejectCount(int, int, @NonNull String, int, int, long);
+    method public void increaseAccessCount(int, int, @NonNull String, @Nullable String, int, int, long);
+    method public void increaseAccessDuration(int, int, @NonNull String, @Nullable String, int, int, long);
+    method public void increaseRejectCount(int, int, @NonNull String, @Nullable String, int, int, long);
     method public void offsetBeginAndEndTime(long);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR;
@@ -282,6 +292,7 @@
   public static final class AppOpsManager.HistoricalOpsRequest.Builder {
     ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build();
+    method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFeatureId(@Nullable String);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>);
     method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String);
@@ -290,6 +301,9 @@
 
   public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable {
     method public int describeContents();
+    method @IntRange(from=0) public int getFeatureCount();
+    method @Nullable public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOps(@NonNull String);
+    method @NonNull public android.app.AppOpsManager.HistoricalFeatureOps getFeatureOpsAt(@IntRange(from=0) int);
     method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String);
     method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int);
     method @IntRange(from=0) public int getOpCount();
@@ -2183,6 +2197,14 @@
     field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
   }
 
+  public final class VibrationAttributes implements android.os.Parcelable {
+    method @Deprecated @NonNull public android.media.AudioAttributes getAudioAttributes();
+  }
+
+  public static final class VibrationAttributes.Builder {
+    ctor public VibrationAttributes.Builder(@NonNull android.media.AudioAttributes, @Nullable android.os.VibrationEffect);
+  }
+
   public abstract class VibrationEffect implements android.os.Parcelable {
     method public static android.os.VibrationEffect get(int);
     method public static android.os.VibrationEffect get(int, boolean);
@@ -2602,7 +2624,7 @@
     field public static final String SERIAL_NUMBER = "serial_number";
     field public static final String SERVICE_CATEGORY = "service_category";
     field public static final String SLOT_INDEX = "slot_index";
-    field public static final String SUB_ID = "sub_id";
+    field public static final String SUBSCRIPTION_ID = "sub_id";
   }
 
   public static final class Telephony.Sms.Intents {
@@ -3170,7 +3192,9 @@
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.NetworkRegistrationInfo> CREATOR;
     field public static final int DOMAIN_CS = 1; // 0x1
+    field public static final int DOMAIN_CS_PS = 3; // 0x3
     field public static final int DOMAIN_PS = 2; // 0x2
+    field public static final int DOMAIN_UNKNOWN = 0; // 0x0
     field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3
     field public static final int REGISTRATION_STATE_HOME = 1; // 0x1
     field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0
@@ -3249,14 +3273,17 @@
   }
 
   public class TelephonyManager {
+    method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting);
     method public int checkCarrierPrivilegesForPackage(String);
     method public int getCarrierIdListVersion();
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method @Nullable public static android.content.ComponentName getDefaultRespondViaMessageApplication(@NonNull android.content.Context, boolean);
+    method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method public int getEmergencyNumberDbVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
     method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getNetworkCountryIso(int);
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+    method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
     method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
     method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String);
@@ -3367,6 +3394,7 @@
     field public static final int DIALSTRING_USSD = 2; // 0x2
     field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
     field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
+    field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
     field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
     field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
     field public static final String EXTRA_CNA = "cna";
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index a077745..08216d9 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -430,6 +430,6 @@
                 + " (Default: touchscreen)");
         out.println("      press (Default: trackball)");
         out.println("      roll <dx> <dy> (Default: trackball)");
-        out.println("      event <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
+        out.println("      motionevent <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
     }
 }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 887d17c..7b96ce9 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -196,18 +196,6 @@
         "libcutils",
         "libstatslog",
     ],
-    target: {
-        android: {
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libutils",
-            ],
-        },
-    },
 }
 
 
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index f9f11b2..109785f 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -152,6 +152,10 @@
     return false;
 }
 
+bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const {
+    return !((*this) == that);
+}
+
 bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
     if (mValues.size() != that.getValues().size()) {
         return false;
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index b9b86ce..654e135 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -71,6 +71,8 @@
 
     std::string toString() const;
 
+    bool operator!=(const HashableDimensionKey& that) const;
+
     bool operator==(const HashableDimensionKey& that) const;
 
     bool operator<(const HashableDimensionKey& that) const;
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 68a51ef..c569bc1 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -284,6 +284,10 @@
     FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
     FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index bb3a094..5ff0f97 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -170,33 +170,32 @@
             mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
             getElapsedRealtimeNs(),
             [this](const ConfigKey& key) {
-                sp<IStatsCompanionService> sc = getStatsCompanionService();
-                auto receiver = mConfigManager->GetConfigReceiver(key);
-                if (sc == nullptr) {
-                    VLOG("Could not find StatsCompanionService");
+                sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+                if (receiver == nullptr) {
+                    VLOG("Could not find a broadcast receiver for %s",
+                        key.ToString().c_str());
                     return false;
-                } else if (receiver == nullptr) {
-                    VLOG("Statscompanion could not find a broadcast receiver for %s",
-                         key.ToString().c_str());
-                    return false;
-                } else {
-                    sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+                } else if (receiver->sendDataBroadcast(
+                           mProcessor->getLastReportTimeNs(key)).isOk()) {
                     return true;
+                } else {
+                    VLOG("Failed to send a broadcast for receiver %s",
+                        key.ToString().c_str());
+                    return false;
                 }
             },
             [this](const int& uid, const vector<int64_t>& activeConfigs) {
-                auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
-                sp<IStatsCompanionService> sc = getStatsCompanionService();
-                if (sc == nullptr) {
-                    VLOG("Could not access statsCompanion");
-                    return false;
-                } else if (receiver == nullptr) {
+                sp<IPendingIntentRef> receiver =
+                    mConfigManager->GetActiveConfigsChangedReceiver(uid);
+                if (receiver == nullptr) {
                     VLOG("Could not find receiver for uid %d", uid);
                     return false;
-                } else {
-                    sc->sendActiveConfigsChangedBroadcast(receiver, activeConfigs);
+                } else if (receiver->sendActiveConfigsChangedBroadcast(activeConfigs).isOk()) {
                     VLOG("StatsService::active configs broadcast succeeded for uid %d" , uid);
                     return true;
+                } else {
+                    VLOG("StatsService::active configs broadcast failed for uid %d" , uid);
+                    return false;
                 }
             });
 
@@ -574,18 +573,19 @@
         return UNKNOWN_ERROR;
     }
     ConfigKey key(uid, StrToInt64(name));
-    auto receiver = mConfigManager->GetConfigReceiver(key);
-    sp<IStatsCompanionService> sc = getStatsCompanionService();
-    if (sc == nullptr) {
-        VLOG("Could not access statsCompanion");
-    } else if (receiver == nullptr) {
-        VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str())
-    } else {
-        sc->sendDataBroadcast(receiver, mProcessor->getLastReportTimeNs(key));
+    sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+    if (receiver == nullptr) {
+        VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
+        return UNKNOWN_ERROR;
+    } else if (receiver->sendDataBroadcast(
+               mProcessor->getLastReportTimeNs(key)).isOk()) {
         VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
              args[2].c_str());
+    } else {
+        VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(),
+             args[2].c_str());
+        return UNKNOWN_ERROR;
     }
-
     return NO_ERROR;
 }
 
@@ -629,15 +629,15 @@
             }
         }
     }
-    auto receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
-    sp<IStatsCompanionService> sc = getStatsCompanionService();
-    if (sc == nullptr) {
-        VLOG("Could not access statsCompanion");
-    } else if (receiver == nullptr) {
+    sp<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+    if (receiver == nullptr) {
         VLOG("Could not find receiver for uid %d", uid);
-    } else {
-        sc->sendActiveConfigsChangedBroadcast(receiver, configIds);
+        return UNKNOWN_ERROR;
+    } else if (receiver->sendActiveConfigsChangedBroadcast(configIds).isOk()) {
         VLOG("StatsService::trigger active configs changed broadcast succeeded for uid %d" , uid);
+    } else {
+        VLOG("StatsService::trigger active configs changed broadcast failed for uid %d", uid);
+        return UNKNOWN_ERROR;
     }
     return NO_ERROR;
 }
@@ -1111,7 +1111,6 @@
     mPullerManager->SetStatsCompanionService(statsCompanion);
     mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
     mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
-    SubscriberReporter::getInstance().setStatsCompanionService(statsCompanion);
     return Status::ok();
 }
 
@@ -1185,23 +1184,21 @@
     return true;
 }
 
-Status StatsService::removeDataFetchOperation(int64_t key, const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
-
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), key);
+Status StatsService::removeDataFetchOperation(int64_t key,
+                                              const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
+    ConfigKey configKey(callingUid, key);
     mConfigManager->RemoveConfigReceiver(configKey);
     return Status::ok();
 }
 
 Status StatsService::setDataFetchOperation(int64_t key,
-                                           const sp<android::IBinder>& intentSender,
-                                           const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+                                           const sp<IPendingIntentRef>& pir,
+                                           const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), key);
-    mConfigManager->SetConfigReceiver(configKey, intentSender);
+    ConfigKey configKey(callingUid, key);
+    mConfigManager->SetConfigReceiver(configKey, pir);
     if (StorageManager::hasConfigMetricsReport(configKey)) {
         VLOG("StatsService::setDataFetchOperation marking configKey %s to dump reports on disk",
              configKey.ToString().c_str());
@@ -1210,27 +1207,24 @@
     return Status::ok();
 }
 
-Status StatsService::setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
-                                                      const String16& packageName,
+Status StatsService::setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+                                                      const int32_t callingUid,
                                                       vector<int64_t>* output) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    int uid = ipc->getCallingUid();
-    mConfigManager->SetActiveConfigsChangedReceiver(uid, intentSender);
+    mConfigManager->SetActiveConfigsChangedReceiver(callingUid, pir);
     if (output != nullptr) {
-        mProcessor->GetActiveConfigs(uid, *output);
+        mProcessor->GetActiveConfigs(callingUid, *output);
     } else {
         ALOGW("StatsService::setActiveConfigsChanged output was nullptr");
     }
     return Status::ok();
 }
 
-Status StatsService::removeActiveConfigsChangedOperation(const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+Status StatsService::removeActiveConfigsChangedOperation(const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
 
-    IPCThreadState* ipc = IPCThreadState::self();
-    mConfigManager->RemoveActiveConfigsChangedReceiver(ipc->getCallingUid());
+    mConfigManager->RemoveActiveConfigsChangedReceiver(callingUid);
     return Status::ok();
 }
 
@@ -1246,26 +1240,24 @@
 
 Status StatsService::setBroadcastSubscriber(int64_t configId,
                                             int64_t subscriberId,
-                                            const sp<android::IBinder>& intentSender,
-                                            const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+                                            const sp<IPendingIntentRef>& pir,
+                                            const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::setBroadcastSubscriber called.");
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), configId);
+    ConfigKey configKey(callingUid, configId);
     SubscriberReporter::getInstance()
-            .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+            .setBroadcastSubscriber(configKey, subscriberId, pir);
     return Status::ok();
 }
 
 Status StatsService::unsetBroadcastSubscriber(int64_t configId,
                                               int64_t subscriberId,
-                                              const String16& packageName) {
-    ENFORCE_DUMP_AND_USAGE_STATS(packageName);
+                                              const int32_t callingUid) {
+    ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::unsetBroadcastSubscriber called.");
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), configId);
+    ConfigKey configKey(callingUid, configId);
     SubscriberReporter::getInstance()
             .unsetBroadcastSubscriber(configKey, subscriberId);
     return Status::ok();
@@ -1631,7 +1623,6 @@
     }
     mAnomalyAlarmMonitor->setStatsCompanionService(nullptr);
     mPeriodicAlarmMonitor->setStatsCompanionService(nullptr);
-    SubscriberReporter::getInstance().setStatsCompanionService(nullptr);
     mPullerManager->SetStatsCompanionService(nullptr);
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index de55ca9..0565f3c 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -30,6 +30,7 @@
 #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>
 #include <android/os/IStatsd.h>
 #include <binder/IResultReceiver.h>
@@ -121,26 +122,26 @@
      * Binder call to let clients register the data fetch operation for a configuration.
      */
     virtual Status setDataFetchOperation(int64_t key,
-                                         const sp<android::IBinder>& intentSender,
-                                         const String16& packageName) override;
+                                         const sp<IPendingIntentRef>& pir,
+                                         const int32_t callingUid) override;
 
     /**
      * Binder call to remove the data fetch operation for the specified config key.
      */
     virtual Status removeDataFetchOperation(int64_t key,
-                                            const String16& packageName) override;
+                                            const int32_t callingUid) override;
 
     /**
      * Binder call to let clients register the active configs changed operation.
      */
-    virtual Status setActiveConfigsChangedOperation(const sp<android::IBinder>& intentSender,
-                                                    const String16& packageName,
+    virtual Status setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+                                                    const int32_t callingUid,
                                                     vector<int64_t>* output) override;
 
     /**
      * Binder call to remove the active configs changed operation for the specified package..
      */
-    virtual Status removeActiveConfigsChangedOperation(const String16& packageName) override;
+    virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
     /**
      * Binder call to allow clients to remove the specified configuration.
      */
@@ -148,20 +149,19 @@
                                        const String16& packageName) override;
 
     /**
-     * Binder call to associate the given config's subscriberId with the given intentSender.
-     * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
+     * Binder call to associate the given config's subscriberId with the given pendingIntentRef.
      */
     virtual Status setBroadcastSubscriber(int64_t configId,
                                           int64_t subscriberId,
-                                          const sp<android::IBinder>& intentSender,
-                                          const String16& packageName) override;
+                                          const sp<IPendingIntentRef>& pir,
+                                          const int32_t callingUid) override;
 
     /**
-     * Binder call to unassociate the given config's subscriberId with any intentSender.
+     * Binder call to unassociate the given config's subscriberId with any pendingIntentRef.
      */
     virtual Status unsetBroadcastSubscriber(int64_t configId,
                                             int64_t subscriberId,
-                                            const String16& packageName) override;
+                                            const int32_t callingUid) override;
 
     /** Inform statsCompanion that statsd is ready. */
     virtual void sayHiToStatsCompanion();
diff --git a/cmds/statsd/src/atom_field_options.proto b/cmds/statsd/src/atom_field_options.proto
index 16c936c..6d2bd04 100644
--- a/cmds/statsd/src/atom_field_options.proto
+++ b/cmds/statsd/src/atom_field_options.proto
@@ -85,5 +85,5 @@
 
     optional bool allow_from_any_uid = 50003 [default = false];
 
-    optional string log_from_module = 50004;
-}
\ No newline at end of file
+    optional string module = 50004;
+}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2270974..19b9709 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -112,9 +112,9 @@
         TouchEventReported touch_event_reported = 34;
         WakeupAlarmOccurred wakeup_alarm_occurred = 35;
         KernelWakeupReported kernel_wakeup_reported = 36;
-        WifiLockStateChanged wifi_lock_state_changed = 37 [(log_from_module) = "wifi"];
-        WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(log_from_module) = "wifi"];
-        WifiScanStateChanged wifi_scan_state_changed = 39 [(log_from_module) = "wifi"];
+        WifiLockStateChanged wifi_lock_state_changed = 37 [(module) = "wifi"];
+        WifiSignalStrengthChanged wifi_signal_strength_changed = 38 [(module) = "wifi"];
+        WifiScanStateChanged wifi_scan_state_changed = 39 [(module) = "wifi"];
         PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
         SettingChanged setting_changed = 41;
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
@@ -128,7 +128,7 @@
         AppStartFullyDrawn app_start_fully_drawn = 50;
         LmkKillOccurred lmk_kill_occurred = 51;
         PictureInPictureStateChanged picture_in_picture_state_changed = 52;
-        WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(log_from_module) = "wifi"];
+        WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53 [(module) = "wifi"];
         LmkStateChanged lmk_state_changed = 54;
         AppStartMemoryStateCaptured app_start_memory_state_captured = 55;
         ShutdownSequenceReported shutdown_sequence_reported = 56;
@@ -182,39 +182,27 @@
         FlagFlipUpdateOccurred flag_flip_update_occurred = 101;
         BinaryPushStateChanged binary_push_state_changed = 102;
         DevicePolicyEvent device_policy_event = 103;
-        DocsUIFileOperationCanceledReported docs_ui_file_op_canceled =
-            104 [(log_from_module) = "docsui"];
-        DocsUIFileOperationCopyMoveModeReported
-            docs_ui_file_op_copy_move_mode_reported =
-            105 [(log_from_module) = "docsui"];
-        DocsUIFileOperationFailureReported docs_ui_file_op_failure =
-            106 [(log_from_module) = "docsui"];
-        DocsUIFileOperationReported docs_ui_provider_file_op =
-            107 [(log_from_module) = "docsui"];
-        DocsUIInvalidScopedAccessRequestReported
-            docs_ui_invalid_scoped_access_request =
-            108 [(log_from_module) = "docsui"];
-        DocsUILaunchReported docs_ui_launch_reported =
-            109 [(log_from_module) = "docsui"];
-        DocsUIRootVisitedReported docs_ui_root_visited =
-            110 [(log_from_module) = "docsui"];
-        DocsUIStartupMsReported docs_ui_startup_ms =
-            111 [(log_from_module) = "docsui"];
-        DocsUIUserActionReported docs_ui_user_action_reported =
-            112 [(log_from_module) = "docsui"];
+        DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104 [(module) = "docsui"];
+        DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported =
+            105 [(module) = "docsui"];
+        DocsUIFileOperationFailureReported docs_ui_file_op_failure = 106 [(module) = "docsui"];
+        DocsUIFileOperationReported docs_ui_provider_file_op = 107 [(module) = "docsui"];
+        DocsUIInvalidScopedAccessRequestReported docs_ui_invalid_scoped_access_request =
+            108 [(module) = "docsui"];
+        DocsUILaunchReported docs_ui_launch_reported = 109 [(module) = "docsui"];
+        DocsUIRootVisitedReported docs_ui_root_visited = 110 [(module) = "docsui"];
+        DocsUIStartupMsReported docs_ui_startup_ms = 111 [(module) = "docsui"];
+        DocsUIUserActionReported docs_ui_user_action_reported = 112 [(module) = "docsui"];
         WifiEnabledStateChanged wifi_enabled_state_changed = 113;
         WifiRunningStateChanged wifi_running_state_changed = 114;
         AppCompacted app_compacted = 115;
-        NetworkDnsEventReported network_dns_event_reported = 116 [(log_from_module) = "resolv"];
+        NetworkDnsEventReported network_dns_event_reported = 116 [(module) = "resolv"];
         DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported =
-            117 [(log_from_module) = "docsui"];
-        DocsUIPickResultReported docs_ui_pick_result_reported =
-            118 [(log_from_module) = "docsui"];
-        DocsUISearchModeReported docs_ui_search_mode_reported =
-            119 [(log_from_module) = "docsui"];
-        DocsUISearchTypeReported docs_ui_search_type_reported =
-            120 [(log_from_module) = "docsui"];
-        DataStallEvent data_stall_event = 121 [(log_from_module) = "network_stack"];
+            117 [(module) = "docsui"];
+        DocsUIPickResultReported docs_ui_pick_result_reported = 118 [(module) = "docsui"];
+        DocsUISearchModeReported docs_ui_search_mode_reported = 119 [(module) = "docsui"];
+        DocsUISearchTypeReported docs_ui_search_type_reported = 120 [(module) = "docsui"];
+        DataStallEvent data_stall_event = 121 [(module) = "network_stack"];
         RescuePartyResetReported rescue_party_reset_reported = 122;
         SignedConfigReported signed_config_reported = 123;
         GnssNiEventReported gnss_ni_event_reported = 124;
@@ -264,7 +252,7 @@
         ScreenTimeoutExtensionReported screen_timeout_extension_reported = 168;
         ProcessStartTime process_start_time = 169;
         PermissionGrantRequestResultReported permission_grant_request_result_reported =
-            170 [(log_from_module) = "permissioncontroller"];
+            170 [(module) = "permissioncontroller"];
         BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
         DeviceIdentifierAccessDenied device_identifier_access_denied = 172;
         BubbleDeveloperErrorReported bubble_developer_error_reported = 173;
@@ -273,21 +261,21 @@
         AssistGestureProgressReported assist_gesture_progress_reported = 176;
         TouchGestureClassified touch_gesture_classified = 177;
         HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true];
-        StyleUIChanged style_ui_changed = 179 [(log_from_module) = "style"];
+        StyleUIChanged style_ui_changed = 179 [(module) = "style"];
         PrivacyIndicatorsInteracted privacy_indicators_interacted =
-            180 [(log_from_module) = "permissioncontroller"];
+            180 [(module) = "permissioncontroller"];
         AppInstallOnExternalStorageReported app_install_on_external_storage_reported = 181;
-        NetworkStackReported network_stack_reported = 182 [(log_from_module) = "network_stack"];
+        NetworkStackReported network_stack_reported = 182 [(module) = "network_stack"];
         AppMovedStorageReported app_moved_storage_reported = 183;
         BiometricEnrolled biometric_enrolled = 184;
         SystemServerWatchdogOccurred system_server_watchdog_occurred = 185;
         TombStoneOccurred tomb_stone_occurred = 186;
         BluetoothClassOfDeviceReported bluetooth_class_of_device_reported = 187;
         IntelligenceEventReported intelligence_event_reported =
-            188 [(log_from_module) = "intelligence"];
+            188 [(module) = "intelligence"];
         ThermalThrottlingSeverityStateChanged thermal_throttling_severity_state_changed = 189;
         RoleRequestResultReported role_request_result_reported =
-            190 [(log_from_module) = "permissioncontroller"];
+            190 [(module) = "permissioncontroller"];
         MediametricsAudiopolicyReported mediametrics_audiopolicy_reported = 191;
         MediametricsAudiorecordReported mediametrics_audiorecord_reported = 192;
         MediametricsAudiothreadReported mediametrics_audiothread_reported = 193;
@@ -301,36 +289,32 @@
         MediametricsDrmManagerReported mediametrics_drmmanager_reported = 201;
         CarPowerStateChanged car_power_state_changed = 203;
         GarageModeInfo garage_mode_info = 204;
-        TestAtomReported test_atom_reported = 205 [(log_from_module) = "cts"];
+        TestAtomReported test_atom_reported = 205 [(module) = "cts"];
         ContentCaptureCallerMismatchReported content_capture_caller_mismatch_reported = 206;
         ContentCaptureServiceEvents content_capture_service_events = 207;
         ContentCaptureSessionEvents content_capture_session_events = 208;
         ContentCaptureFlushed content_capture_flushed = 209;
         LocationManagerApiUsageReported location_manager_api_usage_reported = 210;
         ReviewPermissionsFragmentResultReported review_permissions_fragment_result_reported =
-            211 [(log_from_module) = "permissioncontroller"];
+            211 [(module) = "permissioncontroller"];
         RuntimePermissionsUpgradeResult runtime_permissions_upgrade_result =
-            212 [(log_from_module) = "permissioncontroller"];
+            212 [(module) = "permissioncontroller"];
         GrantPermissionsActivityButtonActions grant_permissions_activity_button_actions =
-            213 [(log_from_module) = "permissioncontroller"];
+            213 [(module) = "permissioncontroller"];
         LocationAccessCheckNotificationAction location_access_check_notification_action =
-            214 [(log_from_module) = "permissioncontroller"];
+            214 [(module) = "permissioncontroller"];
         AppPermissionFragmentActionReported app_permission_fragment_action_reported =
-            215 [(log_from_module) = "permissioncontroller"];
+            215 [(module) = "permissioncontroller"];
         AppPermissionFragmentViewed app_permission_fragment_viewed =
-            216 [(log_from_module) = "permissioncontroller"];
+            216 [(module) = "permissioncontroller"];
         AppPermissionsFragmentViewed app_permissions_fragment_viewed =
-            217 [(log_from_module) = "permissioncontroller"];
+            217 [(module) = "permissioncontroller"];
         PermissionAppsFragmentViewed permission_apps_fragment_viewed =
-            218  [(log_from_module) = "permissioncontroller"];
-        TextSelectionEvent text_selection_event =
-            219  [(log_from_module) = "textclassifier"];
-        TextLinkifyEvent text_linkify_event =
-            220  [(log_from_module) = "textclassifier"];
-        ConversationActionsEvent conversation_actions_event =
-            221  [(log_from_module) = "textclassifier"];
-        LanguageDetectionEvent language_detection_event =
-            222  [(log_from_module) = "textclassifier"];
+            218  [(module) = "permissioncontroller"];
+        TextSelectionEvent text_selection_event = 219  [(module) = "textclassifier"];
+        TextLinkifyEvent text_linkify_event = 220  [(module) = "textclassifier"];
+        ConversationActionsEvent conversation_actions_event = 221  [(module) = "textclassifier"];
+        LanguageDetectionEvent language_detection_event = 222  [(module) = "textclassifier"];
         ExclusionRectStateChanged exclusion_rect_state_changed = 223;
         BackGesture back_gesture_reported_reported = 224;
         UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
@@ -338,21 +322,17 @@
         CameraActionEvent camera_action_event = 227;
         AppCompatibilityChangeReported app_compatibility_change_reported =
             228 [(allow_from_any_uid) = true];
-        PerfettoUploaded perfetto_uploaded =
-            229 [(log_from_module) = "perfetto"];
+        PerfettoUploaded perfetto_uploaded = 229 [(module) = "perfetto"];
         VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
         GpsLocationStatusReported gps_location_status_reported = 231;
         GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232;
-        MediaProviderScanEvent media_provider_scan_event =
-            233 [(log_from_module) = "mediaprovider"];
-        MediaProviderDeletionEvent media_provider_deletion_event =
-            234 [(log_from_module) = "mediaprovider"];
+        MediaProviderScanEvent media_provider_scan_event = 233 [(module) = "mediaprovider"];
+        MediaProviderDeletionEvent media_provider_deletion_event = 234 [(module) = "mediaprovider"];
         MediaProviderPermissionEvent media_provider_permission_event =
-            235 [(log_from_module) = "mediaprovider"];
-        MediaProviderSchemaChange media_provider_schema_change =
-            236 [(log_from_module) = "mediaprovider"];
+            235 [(module) = "mediaprovider"];
+        MediaProviderSchemaChange media_provider_schema_change = 236 [(module) = "mediaprovider"];
         MediaProviderIdleMaintenance media_provider_idle_maintenance =
-            237 [(log_from_module) = "mediaprovider"];
+            237 [(module) = "mediaprovider"];
     }
 
     // Pulled events will start at field 10000.
@@ -6711,6 +6691,9 @@
 
     // App is not doing pre-rotation correctly.
     optional bool false_prerotation = 7;
+
+    // App creates GLESv1 context.
+    optional bool gles_1_in_use = 8;
 }
 
 /*
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index fc949b4..55d73c1 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -46,6 +46,41 @@
 using android::base::StringPrintf;
 using std::unique_ptr;
 
+class ConfigReceiverDeathRecipient : public android::IBinder::DeathRecipient {
+    public:
+        ConfigReceiverDeathRecipient(sp<ConfigManager> configManager, const ConfigKey& configKey):
+            mConfigManager(configManager),
+            mConfigKey(configKey) {}
+        ~ConfigReceiverDeathRecipient() override = default;
+    private:
+        sp<ConfigManager> mConfigManager;
+        ConfigKey mConfigKey;
+
+    void binderDied(const android::wp<android::IBinder>& who) override {
+        if (IInterface::asBinder(mConfigManager->GetConfigReceiver(mConfigKey)) == who.promote()) {
+             mConfigManager->RemoveConfigReceiver(mConfigKey);
+        }
+    }
+};
+
+class ActiveConfigChangedReceiverDeathRecipient : public android::IBinder::DeathRecipient {
+    public:
+        ActiveConfigChangedReceiverDeathRecipient(sp<ConfigManager> configManager, const int uid):
+            mConfigManager(configManager),
+            mUid(uid) {}
+        ~ActiveConfigChangedReceiverDeathRecipient() override = default;
+    private:
+        sp<ConfigManager> mConfigManager;
+        int mUid;
+
+    void binderDied(const android::wp<android::IBinder>& who) override {
+        if (IInterface::asBinder(mConfigManager->GetActiveConfigsChangedReceiver(mUid))
+              == who.promote()) {
+            mConfigManager->RemoveActiveConfigsChangedReceiver(mUid);
+        }
+    }
+};
+
 ConfigManager::ConfigManager() {
 }
 
@@ -118,9 +153,11 @@
     }
 }
 
-void ConfigManager::SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender) {
+void ConfigManager::SetConfigReceiver(const ConfigKey& key,
+                                      const sp<IPendingIntentRef>& pir) {
     lock_guard<mutex> lock(mMutex);
-    mConfigReceivers[key] = intentSender;
+    mConfigReceivers[key] = pir;
+    IInterface::asBinder(pir)->linkToDeath(new ConfigReceiverDeathRecipient(this, key));
 }
 
 void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -129,9 +166,11 @@
 }
 
 void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
-                                                    const sp<IBinder>& intentSender) {
+                                                    const sp<IPendingIntentRef>& pir) {
     lock_guard<mutex> lock(mMutex);
-    mActiveConfigsChangedReceivers[uid] = intentSender;
+    mActiveConfigsChangedReceivers[uid] = pir;
+    IInterface::asBinder(pir)->linkToDeath(
+        new ActiveConfigChangedReceiverDeathRecipient(this, uid));
 }
 
 void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -266,7 +305,7 @@
     return ret;
 }
 
-const sp<android::IBinder> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const sp<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
     lock_guard<mutex> lock(mMutex);
 
     auto it = mConfigReceivers.find(key);
@@ -277,7 +316,7 @@
     }
 }
 
-const sp<android::IBinder> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const sp<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
     lock_guard<mutex> lock(mMutex);
 
     auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index c064a51..88e864a 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -16,10 +16,10 @@
 
 #pragma once
 
-#include "binder/IBinder.h"
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
 
+#include <android/os/IPendingIntentRef.h>
 #include <map>
 #include <mutex>
 #include <set>
@@ -64,12 +64,12 @@
     /**
      * Sets the broadcast receiver for a configuration key.
      */
-    void SetConfigReceiver(const ConfigKey& key, const sp<IBinder>& intentSender);
+    void SetConfigReceiver(const ConfigKey& key, const sp<IPendingIntentRef>& pir);
 
     /**
      * Returns the package name and class name representing the broadcast receiver for this config.
      */
-    const sp<android::IBinder> GetConfigReceiver(const ConfigKey& key) const;
+    const sp<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
 
     /**
      * Returns all config keys registered.
@@ -85,13 +85,13 @@
      * Sets the broadcast receiver that is notified whenever the list of active configs
      * changes for this uid.
      */
-    void SetActiveConfigsChangedReceiver(const int uid, const sp<IBinder>& intentSender);
+    void SetActiveConfigsChangedReceiver(const int uid, const sp<IPendingIntentRef>& pir);
 
     /**
      * Returns the broadcast receiver for active configs changed for this uid.
      */
 
-    const sp<IBinder> GetActiveConfigsChangedReceiver(const int uid) const;
+    const sp<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
 
     /**
      * Erase any active configs changed broadcast receiver associated with this uid.
@@ -141,16 +141,15 @@
     std::map<int, std::set<ConfigKey>> mConfigs;
 
     /**
-     * Each config key can be subscribed by up to one receiver, specified as IBinder from
-     * PendingIntent.
+     * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
      */
-    std::map<ConfigKey, sp<android::IBinder>> mConfigReceivers;
+    std::map<ConfigKey, sp<IPendingIntentRef>> mConfigReceivers;
 
     /**
      * Each uid can be subscribed by up to one receiver to notify that the list of active configs
-     * for this uid has changed. The receiver is specified as IBinder from PendingIntent.
+     * for this uid has changed. The receiver is specified as IPendingIntentRef.
      */
-     std::map<int, sp<android::IBinder>> mActiveConfigsChangedReceivers;
+     std::map<int, sp<IPendingIntentRef>> mActiveConfigsChangedReceivers;
 
     /**
      * The ConfigListeners that will be told about changes.
diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp
index d38b87f..3229ba8 100644
--- a/cmds/statsd/src/external/GpuStatsPuller.cpp
+++ b/cmds/statsd/src/external/GpuStatsPuller.cpp
@@ -103,6 +103,7 @@
         }
         if (!event->write(info.cpuVulkanInUse)) return false;
         if (!event->write(info.falsePrerotation)) return false;
+        if (!event->write(info.gles1InUse)) return false;
         event->init();
         data->emplace_back(event);
     }
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index c1f95ee..21ffff3 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -277,8 +277,8 @@
 
 void CountMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const MetricDimensionKey& eventKey,
-        const ConditionKey& conditionKey, bool condition,
-        const LogEvent& event) {
+        const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+        const map<int, HashableDimensionKey>& statePrimaryKeys) {
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
     flushIfNeededLocked(eventTimeNs);
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 7b6c7e0..a4711e8 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -59,8 +59,8 @@
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
-            const ConditionKey& conditionKey, bool condition,
-            const LogEvent& event) override;
+            const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+            const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
 
 private:
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index fee5e6e..35c6d37 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -541,8 +541,8 @@
 
 void DurationMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const MetricDimensionKey& eventKey,
-        const ConditionKey& conditionKeys, bool condition,
-        const LogEvent& event) {
+        const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
+        const map<int, HashableDimensionKey>& statePrimaryKeys) {
     ALOGW("Not used in duration tracker.");
 }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 7457d7f..45908fb 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -59,8 +59,8 @@
 
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
-            const ConditionKey& conditionKeys, bool condition,
-            const LogEvent& event) override;
+            const ConditionKey& conditionKeys, bool condition, const LogEvent& event,
+            const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
 
 private:
     void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 32eb077..6833f8d 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -143,8 +143,8 @@
 
 void EventMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const MetricDimensionKey& eventKey,
-        const ConditionKey& conditionKey, bool condition,
-        const LogEvent& event) {
+        const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+        const map<int, HashableDimensionKey>& statePrimaryKeys) {
     if (!condition) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index dca37e8..e8f2119 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,8 +47,8 @@
 private:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
-            const ConditionKey& conditionKey, bool condition,
-            const LogEvent& event) override;
+            const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+            const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 4ab6ec3..4ab6fd4 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -449,8 +449,8 @@
 
 void GaugeMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const MetricDimensionKey& eventKey,
-        const ConditionKey& conditionKey, bool condition,
-        const LogEvent& event) {
+        const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+        const map<int, HashableDimensionKey>& statePrimaryKeys) {
     if (condition == false) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 12dcaa4..284bcc5 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -95,8 +95,8 @@
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
-            const ConditionKey& conditionKey, bool condition,
-            const LogEvent& event) override;
+            const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+            const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
 
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index cf1d2f3..5c29cb3 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -133,8 +133,8 @@
     HashableDimensionKey dimensionInWhat;
     filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
     MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey);
-    onMatchedLogEventInternalLocked(
-            matcherIndex, metricKey, conditionKey, condition, event);
+    onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event,
+                                    statePrimaryKeys);
 }
 
 bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) {
@@ -269,6 +269,7 @@
                                          FieldValue* value) {
     if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
         value->mValue = Value(StateTracker::kStateUnknown);
+        value->mField.setTag(atomId);
         ALOGW("StateTracker not found for state atom %d", atomId);
         return;
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 30675fc..99f0c64 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -330,8 +330,8 @@
      */
     virtual void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
-            const ConditionKey& conditionKey, bool condition,
-            const LogEvent& event) = 0;
+            const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+            const map<int, HashableDimensionKey>& statePrimaryKeys) = 0;
 
     // Consume the parsed stats log entry that already matched the "what" of the metric.
     virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
@@ -475,6 +475,10 @@
     FRIEND_TEST(StatsLogProcessorTest,
             TestActivationOnBootMultipleActivationsDifferentActivationTypes);
     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 1fda696..6d20822 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -289,6 +289,10 @@
     FRIEND_TEST(DurationMetricE2eTest, TestWithCondition);
     FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition);
+
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState);
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions);
+    FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index d8f399f..d2db6e9 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -66,6 +66,7 @@
 const int FIELD_ID_DIMENSION_IN_WHAT = 1;
 const int FIELD_ID_BUCKET_INFO = 3;
 const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
+const int FIELD_ID_SLICE_BY_STATE = 6;
 // for ValueBucketInfo
 const int FIELD_ID_VALUE_INDEX = 1;
 const int FIELD_ID_VALUE_LONG = 2;
@@ -146,6 +147,14 @@
         mConditionSliced = true;
     }
 
+    for (const auto& stateLink : metric.state_link()) {
+        Metric2State ms;
+        ms.stateAtomId = stateLink.state_atom_id();
+        translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
+        translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+        mMetric2StateLinks.push_back(ms);
+    }
+
     int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs);
     mCurrentBucketNum += numBucketsForward;
 
@@ -181,6 +190,33 @@
     }
 }
 
+void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
+                                         const HashableDimensionKey& primaryKey, int oldState,
+                                         int newState) {
+    VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d",
+         (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(),
+         oldState, newState);
+    // If condition is not true, we do not need to pull for this state change.
+    if (mCondition != ConditionState::kTrue) {
+        return;
+    }
+    bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs;
+    if (isEventLate) {
+        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+             (long long)mCurrentBucketStartTimeNs);
+        invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
+        return;
+    }
+    mStateChangePrimaryKey.first = atomId;
+    mStateChangePrimaryKey.second = primaryKey;
+    if (mIsPulled) {
+        pullAndMatchEventsLocked(eventTimeNs);
+    }
+    mStateChangePrimaryKey.first = 0;
+    mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY;
+    flushIfNeededLocked(eventTimeNs);
+}
+
 void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
                                                            const int64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
@@ -281,6 +317,14 @@
                                            FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput);
         }
 
+        // Then fill slice_by_state.
+        for (auto state : dimensionKey.getStateValuesKey().getValues()) {
+            uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                                     FIELD_ID_SLICE_BY_STATE);
+            writeStateToProto(state, protoOutput);
+            protoOutput->end(stateToken);
+        }
+
         // Then fill bucket_info (ValueBucketInfo).
         for (const auto& bucket : pair.second) {
             uint64_t bucketInfoToken = protoOutput->start(
@@ -300,7 +344,7 @@
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
                                    (long long)bucket.mConditionTrueNs);
             }
-            for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) {
+            for (int i = 0; i < (int)bucket.valueIndex.size(); i++) {
                 int index = bucket.valueIndex[i];
                 const Value& value = bucket.values[i];
                 uint64_t valueToken = protoOutput->start(
@@ -358,9 +402,10 @@
 }
 
 void ValueMetricProducer::resetBase() {
-    for (auto& slice : mCurrentSlicedBucket) {
-        for (auto& interval : slice.second) {
-            interval.hasBase = false;
+    for (auto& slice : mCurrentBaseInfo) {
+        for (auto& baseInfo : slice.second) {
+            baseInfo.hasBase = false;
+            baseInfo.hasCurrentState = false;
         }
     }
     mHasGlobalBase = false;
@@ -558,14 +603,20 @@
             onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
         }
     }
-    // If the new pulled data does not contains some keys we track in our intervals, we need to
-    // reset the base.
+    // If a key that is:
+    // 1. Tracked in mCurrentSlicedBucket and
+    // 2. A superset of the current mStateChangePrimaryKey
+    // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
+    // then we need to reset the base.
     for (auto& slice : mCurrentSlicedBucket) {
-        bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first)
-                != mMatchedMetricDimensionKeys.end();
-        if (!presentInPulledData) {
-            for (auto& interval : slice.second) {
-                interval.hasBase = false;
+        const auto& whatKey = slice.first.getDimensionKeyInWhat();
+        bool presentInPulledData =
+                mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
+        if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
+            auto it = mCurrentBaseInfo.find(whatKey);
+            for (auto& baseInfo : it->second) {
+                baseInfo.hasBase = false;
+                baseInfo.hasCurrentState = false;
             }
         }
     }
@@ -674,17 +725,30 @@
     return false;
 }
 
-void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex,
-                                                          const MetricDimensionKey& eventKey,
-                                                          const ConditionKey& conditionKey,
-                                                          bool condition, const LogEvent& event) {
+void ValueMetricProducer::onMatchedLogEventInternalLocked(
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
+        const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+        const map<int, HashableDimensionKey>& statePrimaryKeys) {
+    auto whatKey = eventKey.getDimensionKeyInWhat();
+    auto stateKey = eventKey.getStateValuesKey();
+
+    // Skip this event if a state changed occurred for a different primary key.
+    auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first);
+    // Check that both the atom id and the primary key are equal.
+    if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) {
+        VLOG("ValueMetric skip event with primary key %s because state change primary key "
+             "is %s",
+             it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str());
+        return;
+    }
+
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
         VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
         return;
     }
-    mMatchedMetricDimensionKeys.insert(eventKey);
+    mMatchedMetricDimensionKeys.insert(whatKey);
 
     if (!mIsPulled) {
         // We cannot flush without doing a pull first.
@@ -709,10 +773,26 @@
     if (hitGuardRailLocked(eventKey)) {
         return;
     }
-    vector<Interval>& multiIntervals = mCurrentSlicedBucket[eventKey];
-    if (multiIntervals.size() < mFieldMatchers.size()) {
+    vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+    if (baseInfos.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
-        multiIntervals.resize(mFieldMatchers.size());
+        baseInfos.resize(mFieldMatchers.size());
+    }
+
+    for (auto baseInfo : baseInfos) {
+        if (!baseInfo.hasCurrentState) {
+            baseInfo.currentState = DEFAULT_DIMENSION_KEY;
+            baseInfo.hasCurrentState = true;
+        }
+    }
+
+    // We need to get the intervals stored with the previous state key so we can
+    // close these value intervals.
+    const auto oldStateKey = baseInfos[0].currentState;
+    vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+    if (intervals.size() < mFieldMatchers.size()) {
+        VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
+        intervals.resize(mFieldMatchers.size());
     }
 
     // We only use anomaly detection under certain cases.
@@ -725,7 +805,8 @@
 
     for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
         const Matcher& matcher = mFieldMatchers[i];
-        Interval& interval = multiIntervals[i];
+        BaseInfo& baseInfo = baseInfos[i];
+        Interval& interval = intervals[i];
         interval.valueIndex = i;
         Value value;
         if (!getDoubleOrLong(event, matcher, value)) {
@@ -736,60 +817,61 @@
         interval.seenNewData = true;
 
         if (mUseDiff) {
-            if (!interval.hasBase) {
+            if (!baseInfo.hasBase) {
                 if (mHasGlobalBase && mUseZeroDefaultBase) {
                     // The bucket has global base. This key does not.
                     // Optionally use zero as base.
-                    interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
-                    interval.hasBase = true;
+                    baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE);
+                    baseInfo.hasBase = true;
                 } else {
                     // no base. just update base and return.
-                    interval.base = value;
-                    interval.hasBase = true;
+                    baseInfo.base = value;
+                    baseInfo.hasBase = true;
                     // If we're missing a base, do not use anomaly detection on incomplete data
                     useAnomalyDetection = false;
-                    // Continue (instead of return) here in order to set interval.base and
-                    // interval.hasBase for other intervals
+                    // Continue (instead of return) here in order to set baseInfo.base and
+                    // baseInfo.hasBase for other baseInfos
                     continue;
                 }
             }
+
             Value diff;
             switch (mValueDirection) {
                 case ValueMetric::INCREASING:
-                    if (value >= interval.base) {
-                        diff = value - interval.base;
+                    if (value >= baseInfo.base) {
+                        diff = value - baseInfo.base;
                     } else if (mUseAbsoluteValueOnReset) {
                         diff = value;
                     } else {
                         VLOG("Unexpected decreasing value");
                         StatsdStats::getInstance().notePullDataError(mPullTagId);
-                        interval.base = value;
+                        baseInfo.base = value;
                         // If we've got bad data, do not use anomaly detection
                         useAnomalyDetection = false;
                         continue;
                     }
                     break;
                 case ValueMetric::DECREASING:
-                    if (interval.base >= value) {
-                        diff = interval.base - value;
+                    if (baseInfo.base >= value) {
+                        diff = baseInfo.base - value;
                     } else if (mUseAbsoluteValueOnReset) {
                         diff = value;
                     } else {
                         VLOG("Unexpected increasing value");
                         StatsdStats::getInstance().notePullDataError(mPullTagId);
-                        interval.base = value;
+                        baseInfo.base = value;
                         // If we've got bad data, do not use anomaly detection
                         useAnomalyDetection = false;
                         continue;
                     }
                     break;
                 case ValueMetric::ANY:
-                    diff = value - interval.base;
+                    diff = value - baseInfo.base;
                     break;
                 default:
                     break;
             }
-            interval.base = value;
+            baseInfo.base = value;
             value = diff;
         }
 
@@ -814,12 +896,13 @@
             interval.hasValue = true;
         }
         interval.sampleSize += 1;
+        baseInfo.currentState = stateKey;
     }
 
     // Only trigger the tracker if all intervals are correct
     if (useAnomalyDetection) {
         // TODO: propgate proper values down stream when anomaly support doubles
-        long wholeBucketVal = multiIntervals[0].value.long_value;
+        long wholeBucketVal = intervals[0].value.long_value;
         auto prev = mCurrentFullBucket.find(eventKey);
         if (prev != mCurrentFullBucket.end()) {
             wholeBucketVal += prev->second;
@@ -953,6 +1036,7 @@
         } else {
             it++;
         }
+        // TODO: remove mCurrentBaseInfo entries when obsolete
     }
 
     mCurrentBucketIsInvalid = false;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4eae99b..19fb694 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -83,11 +83,14 @@
         flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
     };
 
+    void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
+                        int oldState, int newState) override;
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
-            const ConditionKey& conditionKey, bool condition,
-            const LogEvent& event) override;
+            const ConditionKey& conditionKey, bool condition, const LogEvent& event,
+            const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
 
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -144,7 +147,10 @@
     std::vector<Matcher> mFieldMatchers;
 
     // Value fields for matching.
-    std::set<MetricDimensionKey> mMatchedMetricDimensionKeys;
+    std::set<HashableDimensionKey> mMatchedMetricDimensionKeys;
+
+    // Holds the atom id, primary key pair from a state change.
+    pair<int32_t, HashableDimensionKey> mStateChangePrimaryKey;
 
     // tagId for pulled data. -1 if this is not pulled
     const int mPullTagId;
@@ -156,10 +162,6 @@
     typedef struct {
         // Index in multi value aggregation.
         int valueIndex;
-        // Holds current base value of the dimension. Take diff and update if necessary.
-        Value base;
-        // Whether there is a base to diff to.
-        bool hasBase;
         // Current value, depending on the aggregation type.
         Value value;
         // Number of samples collected.
@@ -171,8 +173,21 @@
         bool seenNewData = false;
     } Interval;
 
+    typedef struct {
+        // Holds current base value of the dimension. Take diff and update if necessary.
+        Value base;
+        // Whether there is a base to diff to.
+        bool hasBase;
+        // Last seen state value(s).
+        HashableDimensionKey currentState;
+        // Whether this dimensions in what key has a current state key.
+        bool hasCurrentState;
+    } BaseInfo;
+
     std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
 
+    std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+
     std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
@@ -285,6 +300,9 @@
     FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate);
     FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput);
     FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedState);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions);
     FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 2ad8217..73c1212 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -18,11 +18,12 @@
 #include "Log.h"
 
 #include "metrics_manager_util.h"
-#include "MetricProducer.h"
 
 #include <inttypes.h>
 
 #include "atoms_info.h"
+#include "FieldValue.h"
+#include "MetricProducer.h"
 #include "condition/CombinationConditionTracker.h"
 #include "condition/SimpleConditionTracker.h"
 #include "condition/StateConditionTracker.h"
@@ -173,6 +174,14 @@
     return true;
 }
 
+bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
+                               const vector<Matcher>& dimensionsInWhat) {
+    vector<Matcher> stateMatchers;
+    translateFieldMatcher(stateMatcher, &stateMatchers);
+
+    return subsetDimensions(stateMatchers, dimensionsInWhat);
+}
+
 // Validates a metricActivation and populates state.
 // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
 //      to provide the producer with state about its activators and deactivators.
@@ -669,18 +678,41 @@
             }
         }
 
+        std::vector<int> slicedStateAtoms;
+        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+        if (metric.slice_by_state_size() > 0) {
+            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+                return false;
+            }
+        } else {
+            if (metric.state_link_size() > 0) {
+                ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
+                return false;
+            }
+        }
+
+        // Check that all metric state links are a subset of dimensions_in_what fields.
+        std::vector<Matcher> dimensionsInWhat;
+        translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+        for (const auto& stateLink : metric.state_link()) {
+            if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+                return false;
+            }
+        }
+
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
+        bool success = handleMetricActivation(
+                config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation, eventActivationMap, eventDeactivationMap);
         if (!success) return false;
 
         sp<MetricProducer> valueProducer = new ValueMetricProducer(
                 key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId,
                 timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap,
-                eventDeactivationMap);
+                eventDeactivationMap, slicedStateAtoms, stateGroupMap);
         allMetricProducers.push_back(valueProducer);
     }
 
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index 80d3983..ea776fa 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -28,13 +28,17 @@
     return sStateManager;
 }
 
+void StateManager::clear() {
+    mStateTrackers.clear();
+}
+
 void StateManager::onLogEvent(const LogEvent& event) {
     if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
         mStateTrackers[event.GetTagId()]->onLogEvent(event);
     }
 }
 
-bool StateManager::registerListener(int32_t atomId, wp<StateListener> listener) {
+bool StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
     // Check if state tracker already exists.
     if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
         // Create a new state tracker iff atom is a state atom.
@@ -50,7 +54,7 @@
     return true;
 }
 
-void StateManager::unregisterListener(int32_t atomId, wp<StateListener> listener) {
+void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
     std::unique_lock<std::mutex> lock(mMutex);
 
     // Hold the sp<> until the lock is released so that ~StateTracker() is
@@ -74,7 +78,7 @@
     lock.unlock();
 }
 
-bool StateManager::getStateValue(int32_t atomId, const HashableDimensionKey& key,
+bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key,
                                  FieldValue* output) const {
     auto it = mStateTrackers.find(atomId);
     if (it != mStateTrackers.end()) {
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index a6053e6..8bc2461 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -40,30 +40,33 @@
     // Returns a pointer to the single, shared StateManager object.
     static StateManager& getInstance();
 
+    // Unregisters all listeners and removes all trackers from StateManager.
+    void clear();
+
     // Notifies the correct StateTracker of an event.
     void onLogEvent(const LogEvent& event);
 
     // Returns true if atomId is being tracked and is associated with a state
     // atom. StateManager notifies the correct StateTracker to register listener.
     // If the correct StateTracker does not exist, a new StateTracker is created.
-    bool registerListener(int32_t atomId, wp<StateListener> listener);
+    bool registerListener(const int32_t atomId, wp<StateListener> listener);
 
     // Notifies the correct StateTracker to unregister a listener
     // and removes the tracker if it no longer has any listeners.
-    void unregisterListener(int32_t atomId, wp<StateListener> listener);
+    void unregisterListener(const int32_t atomId, wp<StateListener> listener);
 
     // Returns true if the StateTracker exists and queries for the
     // original state value mapped to the given query key. The state value is
     // stored and output in a FieldValue class.
     // Returns false if the StateTracker doesn't exist.
-    bool getStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
+    bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
                        FieldValue* output) const;
 
     inline int getStateTrackersCount() const {
         return mStateTrackers.size();
     }
 
-    inline int getListenersCount(int32_t atomId) const {
+    inline int getListenersCount(const int32_t atomId) const {
         auto it = mStateTrackers.find(atomId);
         if (it != mStateTrackers.end()) {
             return it->second->getListenersCount();
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 8b4d781..c45274e 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -147,12 +147,14 @@
 message ValueMetricData {
   optional DimensionsValue dimensions_in_what = 1;
 
-  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+  repeated StateValue slice_by_state = 6;
 
   repeated ValueBucketInfo bucket_info = 3;
 
   repeated DimensionsValue dimension_leaf_values_in_what = 4;
 
+  optional DimensionsValue dimensions_in_condition = 2 [deprecated = true];
+
   repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true];
 }
 
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index a22805b..736aa9b 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -290,12 +290,14 @@
 
   optional FieldMatcher dimensions_in_what = 5;
 
-  optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
+  repeated int64 slice_by_state = 18;
 
   optional TimeUnit bucket = 6;
 
   repeated MetricConditionLink links = 7;
 
+  repeated MetricStateLink state_link = 19;
+
   enum AggregationType {
     SUM = 1;
     MIN = 2;
@@ -325,6 +327,8 @@
   optional int32 max_pull_delay_sec = 16 [default = 10];
 
   optional bool split_bucket_for_app_upgrade = 17 [default = true];
+
+  optional FieldMatcher dimensions_in_condition = 9 [deprecated = true];
 }
 
 message Alert {
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 25d2257..a9a105f 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -19,7 +19,6 @@
 
 #include "SubscriberReporter.h"
 
-using android::IBinder;
 using std::lock_guard;
 using std::unordered_map;
 
@@ -29,12 +28,32 @@
 
 using std::vector;
 
+class BroadcastSubscriberDeathRecipient : public android::IBinder::DeathRecipient {
+    public:
+        BroadcastSubscriberDeathRecipient(const ConfigKey& configKey, int64_t subscriberId):
+            mConfigKey(configKey),
+            mSubscriberId(subscriberId) {}
+        ~BroadcastSubscriberDeathRecipient() override = default;
+    private:
+        ConfigKey mConfigKey;
+        int64_t mSubscriberId;
+
+    void binderDied(const android::wp<android::IBinder>& who) override {
+        if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber(
+              mConfigKey, mSubscriberId)) == who.promote()) {
+            SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId);
+        }
+    }
+};
+
 void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
                                                 int64_t subscriberId,
-                                                const sp<IBinder>& intentSender) {
+                                                const sp<IPendingIntentRef>& pir) {
     VLOG("SubscriberReporter::setBroadcastSubscriber called.");
     lock_guard<std::mutex> lock(mLock);
-    mIntentMap[configKey][subscriberId] = intentSender;
+    mIntentMap[configKey][subscriberId] = pir;
+    IInterface::asBinder(pir)->linkToDeath(
+        new BroadcastSubscriberDeathRecipient(configKey, subscriberId));
 }
 
 void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
@@ -97,18 +116,13 @@
     sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
 }
 
-void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
+void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
                                              const ConfigKey& configKey,
                                              const Subscription& subscription,
                                              const vector<String16>& cookies,
                                              const MetricDimensionKey& dimKey) const {
     VLOG("SubscriberReporter::sendBroadcastLocked called.");
-    if (mStatsCompanionService == nullptr) {
-        ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
-        return;
-    }
-    mStatsCompanionService->sendSubscriberBroadcast(
-            intentSender,
+    pir->sendSubscriberBroadcast(
             configKey.GetUid(),
             configKey.GetId(),
             subscription.id(),
@@ -117,6 +131,20 @@
             getStatsDimensionsValue(dimKey.getDimensionKeyInWhat()));
 }
 
+sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+                                                                 int64_t subscriberId) {
+    lock_guard<std::mutex> lock(mLock);
+    auto subscriberMapIt = mIntentMap.find(configKey);
+    if (subscriberMapIt == mIntentMap.end()) {
+        return nullptr;
+    }
+    auto pirMapIt = subscriberMapIt->second.find(subscriberId);
+    if (pirMapIt == subscriberMapIt->second.end()) {
+        return nullptr;
+    }
+    return pirMapIt->second;
+}
+
 void getStatsDimensionsValueHelper(const vector<FieldValue>& dims, size_t* index, int depth,
                                    int prefix, vector<StatsDimensionsValue>* output) {
     size_t count = dims.size();
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 2a7f771..8ccc8ee 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <android/os/IPendingIntentRef.h>
 #include <android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 
@@ -47,23 +48,11 @@
     void operator=(SubscriberReporter const&) = delete;
 
     /**
-     * Tells SubscriberReporter what IStatsCompanionService to use.
-     * May be nullptr, but SubscriberReporter will not send broadcasts for any calls
-     * to alertBroadcastSubscriber that occur while nullptr.
-     */
-    void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
-        std::lock_guard<std::mutex> lock(mLock);
-        sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
-        mStatsCompanionService = statsCompanionService;
-    }
-
-    /**
      * Stores the given intentSender, associating it with the given (configKey, subscriberId) pair.
-     * intentSender must be convertible into an IntentSender (in Java) using IntentSender(IBinder).
      */
     void setBroadcastSubscriber(const ConfigKey& configKey,
                                 int64_t subscriberId,
-                                const sp<android::IBinder>& intentSender);
+                                const sp<IPendingIntentRef>& pir);
 
     /**
      * Erases any intentSender information from the given (configKey, subscriberId) pair.
@@ -82,6 +71,8 @@
                                   const Subscription& subscription,
                                   const MetricDimensionKey& dimKey) const;
 
+    sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
+
     static StatsDimensionsValue getStatsDimensionsValue(const HashableDimensionKey& dim);
 
 private:
@@ -92,15 +83,15 @@
     /** Binder interface for communicating with StatsCompanionService. */
     sp<IStatsCompanionService> mStatsCompanionService = nullptr;
 
-    /** Maps <ConfigKey, SubscriberId> -> IBinder (which represents an IIntentSender). */
+    /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
     std::unordered_map<ConfigKey,
-            std::unordered_map<int64_t, sp<android::IBinder>>> mIntentMap;
+            std::unordered_map<int64_t, sp<IPendingIntentRef>>> mIntentMap;
 
     /**
      * Sends a broadcast via the given intentSender (using mStatsCompanionService), along
      * with the information in the other parameters.
      */
-    void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
+    void sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
                              const ConfigKey& configKey,
                              const Subscription& subscription,
                              const std::vector<String16>& cookies,
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
index 0f51c1b..15fc468 100644
--- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -27,9 +27,6 @@
 
 #ifdef __ANDROID__
 
-const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
-const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
-
 /**
  * Test a count metric that has one slice_by_state with no primary fields.
  *
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index fb878dc7..e8d2ec5 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -369,6 +369,168 @@
     EXPECT_EQ(1, bucketInfo.values_size());
 }
 
+/**
+ * Test initialization of a simple value metric that is sliced by a state.
+ *
+ * ValueCpuUserTimePerScreenState
+ */
+TEST(ValueMetricE2eTest, TestInitWithSlicedState) {
+    // Create config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto pulledAtomMatcher =
+            CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = pulledAtomMatcher;
+
+    auto screenState = CreateScreenState();
+    *config.add_state() = screenState;
+
+    // Create value metric that slices by screen state without a map.
+    int64_t metricId = 123456;
+    auto valueMetric = config.add_value_metric();
+    valueMetric->set_id(metricId);
+    valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    valueMetric->set_what(pulledAtomMatcher.id());
+    *valueMetric->mutable_value_field() =
+            CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
+    valueMetric->add_slice_by_state(screenState.id());
+    valueMetric->set_max_pull_delay_sec(INT_MAX);
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000LL;
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were initialized correctly.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+    // Check that ValueMetricProducer was initialized correctly.
+    EXPECT_EQ(1U, processor->mMetricsManagers.size());
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(1, metricsManager->mAllMetricProducers.size());
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
+    EXPECT_EQ(0, metricProducer->mStateGroupMap.size());
+}
+
+/**
+ * Test initialization of a value metric that is sliced by state and has
+ * dimensions_in_what.
+ *
+ * ValueCpuUserTimePerUidPerUidProcessState
+ */
+TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) {
+    // Create config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto cpuTimePerUidMatcher =
+            CreateSimpleAtomMatcher("CpuTimePerUidMatcher", android::util::CPU_TIME_PER_UID);
+    *config.add_atom_matcher() = cpuTimePerUidMatcher;
+
+    auto uidProcessState = CreateUidProcessState();
+    *config.add_state() = uidProcessState;
+
+    // Create value metric that slices by screen state with a complete map.
+    int64_t metricId = 123456;
+    auto valueMetric = config.add_value_metric();
+    valueMetric->set_id(metricId);
+    valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    valueMetric->set_what(cpuTimePerUidMatcher.id());
+    *valueMetric->mutable_value_field() =
+            CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
+    *valueMetric->mutable_dimensions_in_what() =
+            CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */});
+    valueMetric->add_slice_by_state(uidProcessState.id());
+    MetricStateLink* stateLink = valueMetric->add_state_link();
+    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+    auto fieldsInWhat = stateLink->mutable_fields_in_what();
+    *fieldsInWhat = CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */});
+    auto fieldsInState = stateLink->mutable_fields_in_state();
+    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+    valueMetric->set_max_pull_delay_sec(INT_MAX);
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were initialized correctly.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+    // Check that ValueMetricProducer was initialized correctly.
+    EXPECT_EQ(1U, processor->mMetricsManagers.size());
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(1, metricsManager->mAllMetricProducers.size());
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0));
+    EXPECT_EQ(0, metricProducer->mStateGroupMap.size());
+}
+
+/**
+ * Test initialization of a value metric that is sliced by state and has
+ * dimensions_in_what.
+ *
+ * ValueCpuUserTimePerUidPerUidProcessState
+ */
+TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) {
+    // Create config.
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto cpuTimePerUidMatcher =
+            CreateSimpleAtomMatcher("CpuTimePerUidMatcher", android::util::CPU_TIME_PER_UID);
+    *config.add_atom_matcher() = cpuTimePerUidMatcher;
+
+    auto uidProcessState = CreateUidProcessState();
+    *config.add_state() = uidProcessState;
+
+    // Create value metric that slices by screen state with a complete map.
+    int64_t metricId = 123456;
+    auto valueMetric = config.add_value_metric();
+    valueMetric->set_id(metricId);
+    valueMetric->set_bucket(TimeUnit::FIVE_MINUTES);
+    valueMetric->set_what(cpuTimePerUidMatcher.id());
+    *valueMetric->mutable_value_field() =
+            CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */});
+    valueMetric->add_slice_by_state(uidProcessState.id());
+    MetricStateLink* stateLink = valueMetric->add_state_link();
+    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+    auto fieldsInWhat = stateLink->mutable_fields_in_what();
+    *fieldsInWhat = CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */});
+    auto fieldsInState = stateLink->mutable_fields_in_state();
+    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+    valueMetric->set_max_pull_delay_sec(INT_MAX);
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
+
+    // No StateTrackers are initialized.
+    EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount());
+
+    // Config initialization fails.
+    EXPECT_EQ(0, processor->mMetricsManagers.size());
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
index 6abdfa3..ae92705 100644
--- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp
@@ -58,8 +58,9 @@
 static const int32_t GLES_VERSION                 = 3;
 static const bool CPU_VULKAN_IN_USE               = true;
 static const bool FALSE_PREROTATION               = true;
+static const bool GLES_1_IN_USE                   = true;
 static const size_t NUMBER_OF_VALUES_GLOBAL       = 13;
-static const size_t NUMBER_OF_VALUES_APP          = 7;
+static const size_t NUMBER_OF_VALUES_APP          = 8;
 // clang-format on
 
 class MockGpuStatsPuller : public GpuStatsPuller {
@@ -153,6 +154,7 @@
     EXPECT_TRUE(event->write(int64VectorToProtoByteString(angleDriverLoadingTime)));
     EXPECT_TRUE(event->write(CPU_VULKAN_IN_USE));
     EXPECT_TRUE(event->write(FALSE_PREROTATION));
+    EXPECT_TRUE(event->write(GLES_1_IN_USE));
     event->init();
     inData.emplace_back(event);
     MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData);
@@ -172,6 +174,7 @@
               outData[0]->getValues()[4].mValue.str_value);
     EXPECT_EQ(CPU_VULKAN_IN_USE, outData[0]->getValues()[5].mValue.int_value);
     EXPECT_EQ(FALSE_PREROTATION, outData[0]->getValues()[6].mValue.int_value);
+    EXPECT_EQ(GLES_1_IN_USE, outData[0]->getValues()[7].mValue.int_value);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index da0a672..92e8241 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -148,6 +148,26 @@
         return valueProducer;
     }
 
+    static sp<ValueMetricProducer> createValueProducerWithState(
+            sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
+            vector<int32_t> slicedStateAtoms,
+            unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
+        UidMap uidMap;
+        SimpleAtomMatcher atomMatcher;
+        atomMatcher.set_atom_id(tagId);
+        sp<EventMatcherWizard> eventMatcherWizard =
+                new EventMatcherWizard({new SimpleLogMatchingTracker(
+                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+        sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+        EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+        EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+        sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
+                kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex,
+                eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
+                {}, slicedStateAtoms, stateGroupMap);
+        return valueProducer;
+    }
+
     static ValueMetric createMetric() {
         ValueMetric metric;
         metric.set_id(metricId);
@@ -163,8 +183,13 @@
         metric.set_condition(StringToId("SCREEN_ON"));
         return metric;
     }
-};
 
+    static ValueMetric createMetricWithState(string state) {
+        ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
+        metric.add_slice_by_state(StringToId(state));
+        return metric;
+    }
+};
 
 /*
  * Tests that the first bucket works correctly
@@ -253,10 +278,12 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::Interval curInterval =
+            valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(8, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -273,9 +300,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(23, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(23, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(12, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -294,9 +322,10 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(36, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(13, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -394,9 +423,8 @@
             }));
 
     sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-            kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-            logEventMatcherIndex, eventMatcherWizard, tagId,
-            bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+            kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex,
+            eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -411,9 +439,10 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(8, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -430,8 +459,8 @@
     // No new data seen, so data has been cleared.
     EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(8, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -447,10 +476,11 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     // the base was reset
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(36, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
@@ -482,9 +512,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -498,8 +529,9 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(10, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -515,8 +547,9 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(36, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(26, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -549,9 +582,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -565,8 +599,9 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(10, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -579,8 +614,9 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(36, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(26, curInterval.value.long_value);
     EXPECT_EQ(1UL, valueProducer->mPastBuckets.size());
@@ -633,9 +669,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // startUpdated:false sum:0 start:100
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(100, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -652,8 +689,9 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(110, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(110, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
 
@@ -663,9 +701,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
-    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, curBaseInfo.hasBase);
 
     valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1});
@@ -879,6 +918,7 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1066,10 +1106,11 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
 
     // startUpdated:true sum:0 start:11
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(11, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(11, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -1084,9 +1125,10 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // tartUpdated:false sum:12
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(23, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(23, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
 
@@ -1103,9 +1145,10 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // startUpdated:false sum:12
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(36, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs});
 }
@@ -1148,16 +1191,18 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(100, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
-    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, curBaseInfo.hasBase);
 
     // Now the alarm is delivered.
     // since the condition turned to off before this pull finish, it has no effect
@@ -1167,7 +1212,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
 
@@ -1220,9 +1266,10 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // startUpdated:false sum:0 start:100
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(100, curInterval.base.long_value);
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -1231,15 +1278,17 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 
     // condition changed to true again, before the pull alarm is delivered
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(130, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(130, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
 
     // Now the alarm is delivered, but it is considered late, the data will be used
@@ -1249,8 +1298,9 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
 
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(140, curInterval.base.long_value);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(10, curInterval.value.long_value);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8});
@@ -1477,8 +1527,9 @@
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
             valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(10, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
@@ -1497,8 +1548,9 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(15, curInterval.base.long_value);
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
     shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
@@ -1508,8 +1560,9 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(15, curInterval.base.long_value);
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
@@ -1550,27 +1603,29 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    ValueMetricProducer::Interval curInterval0 =
-        valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::Interval curInterval1 =
-        valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    EXPECT_EQ(true, curInterval0.hasBase);
-    EXPECT_EQ(10, curInterval0.base.long_value);
-    EXPECT_EQ(false, curInterval0.hasValue);
-    EXPECT_EQ(true, curInterval1.hasBase);
-    EXPECT_EQ(20, curInterval1.base.long_value);
-    EXPECT_EQ(false, curInterval1.hasValue);
+    ValueMetricProducer::Interval curInterval =
+            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(10, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(20, curBaseInfo.base.long_value);
+    EXPECT_EQ(false, curInterval.hasValue);
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
 
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    EXPECT_EQ(true, curInterval0.hasValue);
-    EXPECT_EQ(5, curInterval0.value.long_value);
-    EXPECT_EQ(true, curInterval1.hasValue);
-    EXPECT_EQ(2, curInterval1.value.long_value);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(5, curInterval.value.long_value);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    EXPECT_EQ(true, curInterval.hasValue);
+    EXPECT_EQ(2, curInterval.value.long_value);
 
     // no change in first value field
     shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
@@ -1580,14 +1635,17 @@
     event3->init();
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    EXPECT_EQ(true, curInterval0.hasBase);
-    EXPECT_EQ(15, curInterval0.base.long_value);
-    EXPECT_EQ(true, curInterval0.hasValue);
-    EXPECT_EQ(true, curInterval1.hasBase);
-    EXPECT_EQ(25, curInterval1.base.long_value);
-    EXPECT_EQ(true, curInterval1.hasValue);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(15, curBaseInfo.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(25, curBaseInfo.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
 
     shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15);
     event4->write(1);
@@ -1596,14 +1654,16 @@
     event4->init();
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    EXPECT_EQ(true, curInterval0.hasBase);
-    EXPECT_EQ(15, curInterval0.base.long_value);
-    EXPECT_EQ(true, curInterval0.hasValue);
-    EXPECT_EQ(true, curInterval1.hasBase);
-    EXPECT_EQ(29, curInterval1.base.long_value);
-    EXPECT_EQ(true, curInterval1.hasValue);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(15, curBaseInfo.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(29, curBaseInfo.base.long_value);
+    EXPECT_EQ(true, curInterval.hasValue);
 
     valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
 
@@ -1650,9 +1710,11 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
     auto& interval1 = iter->second[0];
+    auto iterBase = valueProducer->mCurrentBaseInfo.begin();
+    auto& baseInfo1 = iterBase->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(3, interval1.base.long_value);
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(3, baseInfo1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -1672,8 +1734,8 @@
 
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(11, interval1.base.long_value);
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(11, baseInfo1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
 
@@ -1683,11 +1745,19 @@
             break;
         }
     }
+    auto itBase = valueProducer->mCurrentBaseInfo.begin();
+    for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
+        if (itBase != iterBase) {
+            break;
+        }
+    }
     EXPECT_TRUE(it != iter);
+    EXPECT_TRUE(itBase != iterBase);
     auto& interval2 = it->second[0];
+    auto& baseInfo2 = itBase->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(4, interval2.base.long_value);
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(4, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_EQ(4, interval2.value.long_value);
 
@@ -1725,11 +1795,13 @@
             ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
 
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
-    EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(3, interval1.base.long_value);
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto& interval1 = it->second[0];
+    auto& baseInfo1 =
+            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(3, baseInfo1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
@@ -1749,22 +1821,31 @@
 
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(11, interval1.base.long_value);
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(11, baseInfo1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
 
-    auto it = valueProducer->mCurrentSlicedBucket.begin();
-    for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) {
-        if (it != iter) {
+    auto it2 = valueProducer->mCurrentSlicedBucket.begin();
+    for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) {
+        if (it2 != it) {
             break;
         }
     }
-    EXPECT_TRUE(it != iter);
-    auto& interval2 = it->second[0];
-    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(4, interval2.base.long_value);
+    // auto itBase = valueProducer->mCurrentBaseInfo.begin();
+    // for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
+    //     if (itBase != iterBase) {
+    //         break;
+    //     }
+    // }
+    EXPECT_TRUE(it2 != it);
+    // EXPECT_TRUE(itBase != iterBase);
+    auto& interval2 = it2->second[0];
+    auto& baseInfo2 =
+            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+    EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(4, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_EQ(4, interval2.value.long_value);
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
@@ -1779,8 +1860,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
 
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(5, interval2.base.long_value);
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(5, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
@@ -1799,17 +1880,24 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    auto it1 = std::next(valueProducer->mCurrentSlicedBucket.begin())->second[0];
-    EXPECT_EQ(true, it1.hasBase);
-    EXPECT_EQ(13, it1.base.long_value);
-    EXPECT_EQ(false, it1.hasValue);
-    EXPECT_EQ(8, it1.value.long_value);
-    auto it2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, it2.hasBase);
-    EXPECT_EQ(5, it2.base.long_value);
-    EXPECT_EQ(false, it2.hasValue);
-    EXPECT_EQ(5, it2.value.long_value);
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    it2 = std::next(valueProducer->mCurrentSlicedBucket.begin());
+    interval1 = it->second[0];
+    interval2 = it2->second[0];
+    baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(5, baseInfo1.base.long_value);
+    EXPECT_EQ(false, interval1.hasValue);
+    EXPECT_EQ(5, interval1.value.long_value);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
+
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(13, baseInfo2.base.long_value);
+    EXPECT_EQ(false, interval2.hasValue);
+    EXPECT_EQ(8, interval2.value.long_value);
+
     EXPECT_EQ(2UL, valueProducer->mPastBuckets.size());
 }
 
@@ -1839,9 +1927,11 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
     auto& interval1 = iter->second[0];
+    auto iterBase = valueProducer->mCurrentBaseInfo.begin();
+    auto& baseInfo1 = iterBase->second[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(3, interval1.base.long_value);
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(3, baseInfo1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
     vector<shared_ptr<LogEvent>> allData;
@@ -1860,8 +1950,8 @@
 
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    EXPECT_EQ(true, interval1.hasBase);
-    EXPECT_EQ(11, interval1.base.long_value);
+    EXPECT_EQ(true, baseInfo1.hasBase);
+    EXPECT_EQ(11, baseInfo1.base.long_value);
     EXPECT_EQ(false, interval1.hasValue);
     EXPECT_EQ(8, interval1.value.long_value);
     EXPECT_FALSE(interval1.seenNewData);
@@ -1873,11 +1963,19 @@
             break;
         }
     }
+    auto itBase = valueProducer->mCurrentBaseInfo.begin();
+    for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) {
+        if (itBase != iterBase) {
+            break;
+        }
+    }
     EXPECT_TRUE(it != iter);
+    EXPECT_TRUE(itBase != iterBase);
     auto& interval2 = it->second[0];
+    auto& baseInfo2 = itBase->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(4, interval2.base.long_value);
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(4, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
@@ -1890,12 +1988,13 @@
     event1->init();
     allData.push_back(event1);
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
-	// Only one interval left. One was trimmed.
+    // Only one interval left. One was trimmed.
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(5, interval2.base.long_value);
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(5, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs});
@@ -1909,8 +2008,9 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
     interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, interval2.hasBase);
-    EXPECT_EQ(14, interval2.base.long_value);
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, baseInfo2.hasBase);
+    EXPECT_EQ(14, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
     EXPECT_FALSE(interval2.seenNewData);
     ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
@@ -1946,14 +2046,15 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(100, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
 
     vector<shared_ptr<LogEvent>> allData;
     valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
@@ -1983,8 +2084,9 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(100, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(0UL, valueProducer->mPastBuckets.size());
 
@@ -1993,7 +2095,7 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     EXPECT_EQ(false, curInterval.hasValue);
-    EXPECT_EQ(false, curInterval.hasBase);
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
 
@@ -2035,7 +2137,8 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
@@ -2118,8 +2221,9 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(100, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
@@ -2181,8 +2285,9 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(140, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
@@ -2222,7 +2327,8 @@
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 for (int i = 0; i < 2000; i++) {
-                    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+                    shared_ptr<LogEvent> event =
+                            make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
                     event->write(i);
                     event->write(i);
                     event->init();
@@ -2338,8 +2444,9 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(140, curInterval.base.long_value);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
@@ -2429,7 +2536,8 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 
@@ -2523,7 +2631,8 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
@@ -2531,7 +2640,8 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
 }
@@ -2579,7 +2689,8 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
@@ -2589,9 +2700,10 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     // Data is empty, base should be reset.
-    EXPECT_EQ(false, curInterval.hasBase);
-    EXPECT_EQ(5, curInterval.base.long_value);
+    EXPECT_EQ(false, curBaseInfo.hasBase);
+    EXPECT_EQ(5, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 
@@ -2638,12 +2750,14 @@
     // Key 1 should be reset since in not present in the most pull.
     EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     auto iterator = valueProducer->mCurrentSlicedBucket.begin();
-    EXPECT_EQ(true, iterator->second[0].hasBase);
-    EXPECT_EQ(2, iterator->second[0].base.long_value);
+    auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
+    EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
+    EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
     EXPECT_EQ(false, iterator->second[0].hasValue);
     iterator++;
-    EXPECT_EQ(false, iterator->second[0].hasBase);
-    EXPECT_EQ(1, iterator->second[0].base.long_value);
+    baseInfoIter++;
+    EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
+    EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
     EXPECT_EQ(false, iterator->second[0].hasValue);
 
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2715,8 +2829,9 @@
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(true, curInterval.hasBase);
-    EXPECT_EQ(5, curInterval.base.long_value);
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(true, curBaseInfo.hasBase);
+    EXPECT_EQ(5, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
 
     valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
@@ -2827,6 +2942,7 @@
 
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -2918,8 +3034,7 @@
 
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer.onDumpReport(bucketStartTimeNs + 10,
-                               true /* include recent buckets */, true,
+    valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
                                FAST, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
@@ -2970,9 +3085,8 @@
 
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer.onDumpReport(bucket4StartTimeNs,
-                               false /* include recent buckets */, true,
-                               FAST, &strSet, &output);
+    valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST,
+                               &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
     // Previous bucket is part of the report.
@@ -3023,8 +3137,7 @@
 
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer.onDumpReport(bucketStartTimeNs + 10,
-                               true /* include recent buckets */, true,
+    valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
                                NO_TIME_CONSTRAINTS, &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
@@ -3058,15 +3171,15 @@
             // condition becomes true
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(
-                        bucketStartTimeNs + 30, 10));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
                 return true;
             }))
             // condition becomes false
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(
-                        bucketStartTimeNs + 50, 20));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 50, 20));
                 return true;
             }));
     sp<ValueMetricProducer> valueProducer =
@@ -3079,11 +3192,11 @@
     EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
 
-
     // Now the alarm is delivered. Condition is off though.
     vector<shared_ptr<LogEvent>> allData;
     allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110));
@@ -3091,7 +3204,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8});
     curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
 
@@ -3104,8 +3218,8 @@
             // condition becomes true
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(
-                        bucketStartTimeNs + 30, 10));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
                 return true;
             }));
     sp<ValueMetricProducer> valueProducer =
@@ -3122,7 +3236,8 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8});
     ValueMetricProducer::Interval curInterval =
             valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    EXPECT_EQ(false, curInterval.hasBase);
+    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
 
@@ -3153,8 +3268,8 @@
             // condition becomes true
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
-                data->push_back(ValueMetricProducerTestHelper::createEvent(
-                        bucketStartTimeNs + 30, 10));
+                data->push_back(
+                        ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10));
                 return true;
             }))
             .WillOnce(Return(false));
@@ -3768,6 +3883,725 @@
     EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis());
 }
 
+/*
+ * Test metric with a simple sliced state
+ * - Increasing values
+ * - Using diff
+ * - Second field is value field
+ */
+TEST(ValueMetricProducerTest, TestSlicedState) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // ValueMetricProducer initialized.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write("field1");
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Screen state change to ON.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 5);
+                event->write("field1");
+                event->write(5);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Screen state change to OFF.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write("field1");
+                event->write(9);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Screen state change to ON.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
+                event->write("field1");
+                event->write(21);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Dump report requested.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50);
+                event->write("field1");
+                event->write(30);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithState(
+                    pullerManager, metric, {android::util::SCREEN_STATE_CHANGED}, {});
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().clear();
+    StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+    // Bucket status after metric initialized.
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(3, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Bucket status after screen state change kStateUnknown->ON.
+    auto screenEvent = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 5);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(5, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Bucket status after screen state change ON->OFF.
+    screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+                                                bucketStartTimeNs + 10);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(9, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, ON}
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(4, it->second[0].value.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Bucket status after screen state change OFF->ON.
+    screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+                                                bucketStartTimeNs + 15);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(21, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, OFF}
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(12, it->second[0].value.long_value);
+    // Value for dimension, state key {{}, ON}
+    it++;
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(4, it->second[0].value.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
+                                NO_TIME_CONSTRAINTS, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    EXPECT_EQ(3, report.value_metrics().data_size());
+
+    auto data = report.value_metrics().data(0);
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
+
+    data = report.value_metrics().data(1);
+    EXPECT_EQ(1, report.value_metrics().data(1).bucket_info_size());
+    EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+
+    data = report.value_metrics().data(2);
+    EXPECT_EQ(1, report.value_metrics().data(2).bucket_info_size());
+    EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+}
+
+/*
+ * Test metric with sliced state with map
+ * - Increasing values
+ * - Using diff
+ * - Second field is value field
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMap) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // ValueMetricProducer initialized.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write("field1");
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Screen state change to ON.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 5);
+                event->write("field1");
+                event->write(5);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Screen state change to VR.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write("field1");
+                event->write(9);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Screen state change to OFF.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15);
+                event->write("field1");
+                event->write(21);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Dump report requested.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 50);
+                event->write("field1");
+                event->write(30);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    const StateMap& stateMap = CreateScreenStateOnOffMap();
+    const StateMap_StateGroup screenOnGroup = stateMap.group(0);
+    const StateMap_StateGroup screenOffGroup = stateMap.group(1);
+
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    for (auto group : stateMap.group()) {
+        for (auto value : group.value()) {
+            stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id();
+        }
+    }
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithState(
+                    pullerManager, metric, {android::util::SCREEN_STATE_CHANGED}, stateGroupMap);
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().clear();
+    StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
+
+    // Bucket status after metric initialized.
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(3, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, {}}
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Bucket status after screen state change kStateUnknown->ON.
+    auto screenEvent = CreateScreenStateChangedEvent(
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 5);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(5, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Bucket status after screen state change ON->VR (also ON).
+    screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR,
+                                                bucketStartTimeNs + 10);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(9, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, ON GROUP}
+    EXPECT_EQ(screenOnGroup.group_id(),
+              it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(4, it->second[0].value.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Bucket status after screen state change VR->OFF.
+    screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+                                                bucketStartTimeNs + 15);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(21, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{}, ON GROUP}
+    EXPECT_EQ(screenOnGroup.group_id(),
+              it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(16, it->second[0].value.long_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
+                                NO_TIME_CONSTRAINTS, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    EXPECT_EQ(3, report.value_metrics().data_size());
+
+    auto data = report.value_metrics().data(0);
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
+
+    data = report.value_metrics().data(1);
+    EXPECT_EQ(1, report.value_metrics().data(1).bucket_info_size());
+    EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+
+    data = report.value_metrics().data(2);
+    EXPECT_EQ(1, report.value_metrics().data(2).bucket_info_size());
+    EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_group_id());
+    EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+}
+
+/*
+ * Test metric that slices by state with a primary field and has dimensions
+ * - Increasing values
+ * - Using diff
+ * - Second field is value field
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
+    metric.mutable_dimensions_in_what()->set_field(tagId);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    MetricStateLink* stateLink = metric.add_state_link();
+    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+    auto fieldsInWhat = stateLink->mutable_fields_in_what();
+    *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+    auto fieldsInState = stateLink->mutable_fields_in_state();
+    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            // ValueMetricProducer initialized.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write(2 /* uid */);
+                event->write(7);
+                event->init();
+                data->push_back(event);
+
+                event = make_shared<LogEvent>(tagId, bucketStartTimeNs);
+                event->write(1 /* uid */);
+                event->write(3);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Uid 1 process state change from kStateUnknown -> Foreground
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+                event->write(1 /* uid */);
+                event->write(6);
+                event->init();
+                data->push_back(event);
+
+                // This event should be skipped.
+                event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
+                event->write(2 /* uid */);
+                event->write(8);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Uid 2 process state change from kStateUnknown -> Background
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40);
+                event->write(2 /* uid */);
+                event->write(9);
+                event->init();
+                data->push_back(event);
+
+                // This event should be skipped.
+                event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 40);
+                event->write(1 /* uid */);
+                event->write(12);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Uid 1 process state change from Foreground -> Background
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+                event->write(1 /* uid */);
+                event->write(13);
+                event->init();
+                data->push_back(event);
+
+                // This event should be skipped.
+                event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+                event->write(2 /* uid */);
+                event->write(11);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Uid 1 process state change from Background -> Foreground
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40);
+                event->write(1 /* uid */);
+                event->write(17);
+                event->init();
+                data->push_back(event);
+
+                // This event should be skipped.
+                event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 40);
+                event->write(2 /* uid */);
+                event->write(15);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // Dump report pull.
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
+                event->write(2 /* uid */);
+                event->write(20);
+                event->init();
+                data->push_back(event);
+
+                event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
+                event->write(1 /* uid */);
+                event->write(21);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithState(
+                    pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {});
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().clear();
+    StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+    // Bucket status after metric initialized.
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {uid 1}.
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(3, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{uid 1}, kStateUnknown}
+    // TODO(tsaichristine): test equality of state values key
+    // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(false, it->second[0].hasValue);
+    // Base for dimension key {uid 2}
+    it++;
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(7, itBase->second[0].base.long_value);
+    // Value for dimension, state key {{uid 2}, kStateUnknown}
+    // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
+    auto uidProcessEvent = CreateUidProcessStateChangedEvent(
+            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs + 20);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {uid 1}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(6, itBase->second[0].base.long_value);
+    // Value for key {uid 1, kStateUnknown}.
+    // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(3, it->second[0].value.long_value);
+
+    // Base for dimension key {uid 2}
+    it++;
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(7, itBase->second[0].base.long_value);
+    // Value for key {uid 2, kStateUnknown}
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Bucket status after uid 2 process state change kStateUnknown -> Background.
+    uidProcessEvent = CreateUidProcessStateChangedEvent(
+            2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs + 40);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+    EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {uid 1}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(6, itBase->second[0].base.long_value);
+    // Value for key {uid 1, kStateUnknown}.
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(3, it->second[0].value.long_value);
+
+    // Base for dimension key {uid 2}
+    it++;
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(9, itBase->second[0].base.long_value);
+    // Value for key {uid 2, kStateUnknown}
+    // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(2, it->second[0].value.long_value);
+
+    // Pull at end of first bucket.
+    vector<shared_ptr<LogEvent>> allData;
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs);
+    event->write(1 /* uid */);
+    event->write(10);
+    event->init();
+    allData.push_back(event);
+
+    event = make_shared<LogEvent>(tagId, bucket2StartTimeNs);
+    event->write(2 /* uid */);
+    event->write(15);
+    event->init();
+    allData.push_back(event);
+
+    valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+    // Buckets flushed after end of first bucket.
+    // None of the buckets should have a value.
+    EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+    EXPECT_EQ(4UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+    // Base for dimension key {uid 2}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(15, itBase->second[0].base.long_value);
+    // Value for key {uid 2, BACKGROUND}.
+    EXPECT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Base for dimension key {uid 1}
+    it++;
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(10, itBase->second[0].base.long_value);
+    // Value for key {uid 1, kStateUnknown}
+    EXPECT_EQ(0, it->first.getStateValuesKey().getValues().size());
+    // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Value for key {uid 1, FOREGROUND}
+    it++;
+    EXPECT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Value for key {uid 2, kStateUnknown}
+    it++;
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Bucket status after uid 1 process state change from Foreground -> Background.
+    uidProcessEvent = CreateUidProcessStateChangedEvent(
+            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs + 20);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+    EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+    EXPECT_EQ(4UL, valueProducer->mPastBuckets.size());
+    EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+    // Base for dimension key {uid 2}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(15, itBase->second[0].base.long_value);
+    // Value for key {uid 2, BACKGROUND}.
+    EXPECT_EQ(false, it->second[0].hasValue);
+    // Base for dimension key {uid 1}
+    it++;
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(13, itBase->second[0].base.long_value);
+    // Value for key {uid 1, kStateUnknown}
+    EXPECT_EQ(false, it->second[0].hasValue);
+    // Value for key {uid 1, FOREGROUND}
+    it++;
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(3, it->second[0].value.long_value);
+    // Value for key {uid 2, kStateUnknown}
+    it++;
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Bucket status after uid 1 process state change Background->Foreground.
+    uidProcessEvent = CreateUidProcessStateChangedEvent(
+            1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs + 40);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+    EXPECT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+    EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+    // Base for dimension key {uid 2}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(true, itBase->second[0].hasBase);
+    EXPECT_EQ(15, itBase->second[0].base.long_value);
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    it++;
+    EXPECT_EQ(false, it->second[0].hasValue);
+
+    // Base for dimension key {uid 1}
+    it++;
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(17, itBase->second[0].base.long_value);
+    // Value for key {uid 1, BACKGROUND}
+    EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(4, it->second[0].value.long_value);
+    // Value for key {uid 1, FOREGROUND}
+    it++;
+    EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(true, it->second[0].hasValue);
+    EXPECT_EQ(3, it->second[0].value.long_value);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
+                                NO_TIME_CONSTRAINTS, &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    EXPECT_EQ(5, report.value_metrics().data_size());
+
+    auto data = report.value_metrics().data(0);
+    EXPECT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              data.slice_by_state(0).value());
+
+    data = report.value_metrics().data(1);
+    EXPECT_EQ(1, report.value_metrics().data(1).bucket_info_size());
+    EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
+
+    data = report.value_metrics().data(2);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              data.slice_by_state(0).value());
+    EXPECT_EQ(2, report.value_metrics().data(2).bucket_info_size());
+    EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+
+    data = report.value_metrics().data(3);
+    EXPECT_EQ(1, report.value_metrics().data(3).bucket_info_size());
+    EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
+
+    data = report.value_metrics().data(4);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_TRUE(data.slice_by_state(0).has_value());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              data.slice_by_state(0).value());
+    EXPECT_EQ(2, report.value_metrics().data(4).bucket_info_size());
+    EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
+    EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index 395167b..26a3733 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -146,6 +146,7 @@
 TEST(StateManagerTest, TestStateManagerGetInstance) {
     sp<TestStateListener> listener1 = new TestStateListener();
     StateManager& mgr = StateManager::getInstance();
+    mgr.clear();
 
     mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
     EXPECT_EQ(1, mgr.getStateTrackersCount());
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 010c194..9bdfeeb 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -30,6 +30,9 @@
 using android::util::ProtoReader;
 using google::protobuf::RepeatedPtrField;
 
+const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
+const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
+
 // Converts a ProtoOutputStream to a StatsLogReport proto.
 StatsLogReport outputStreamToProto(ProtoOutputStream* proto);
 
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 652669c..4d0acb3 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -14142,7 +14142,6 @@
 HSPLandroid/telephony/ServiceState;->copyFrom(Landroid/telephony/ServiceState;)V
 HSPLandroid/telephony/ServiceState;->describeContents()I
 HSPLandroid/telephony/ServiceState;->equals(Ljava/lang/Object;)Z
-HSPLandroid/telephony/ServiceState;->fillInNotifierBundle(Landroid/os/Bundle;)V
 HSPLandroid/telephony/ServiceState;->getCdmaDefaultRoamingIndicator()I
 HSPLandroid/telephony/ServiceState;->getCdmaEriIconIndex()I
 HSPLandroid/telephony/ServiceState;->getCdmaEriIconMode()I
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3f9f7fb..9aef20b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3107,7 +3107,7 @@
         public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170;
 
         /**
-         * Constant for {@link #importance}: This process is contains services
+         * Constant for {@link #importance}: This process contains services
          * that should remain running.  These are background services apps have
          * started, not something the user is aware of, so they may be killed by
          * the system relatively freely (though it is generally desired that they
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index de7cc9d..032e824 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -278,6 +278,9 @@
     public abstract boolean isBackgroundActivityStartsEnabled();
     public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);
 
+    /** @see com.android.server.am.ActivityManagerService#monitor */
+    public abstract void monitor();
+
     /** Input dispatch timeout to a window, start the ANR process. */
     public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
     public abstract boolean inputDispatchingTimedOut(Object proc, String activityShortComponentName,
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 08f8734..a4f6f57 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3659,7 +3659,7 @@
                     r.activity, Looper.myLooper());
         }
         r.activity.onGetDirectActions(cancellationSignal, (actions) -> {
-            Preconditions.checkNotNull(actions);
+            Objects.requireNonNull(actions);
             Preconditions.checkCollectionElementsNotNull(actions, "actions");
             if (!actions.isEmpty()) {
                 final int actionCount = actions.size();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d168572..4a8e4e2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -51,6 +51,7 @@
 import android.util.ArraySet;
 import android.util.LongSparseArray;
 import android.util.LongSparseLongArray;
+import android.util.Pools;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -849,10 +850,12 @@
     public static final int OP_QUERY_ALL_PACKAGES = 91;
     /** @hide Access all external storage */
     public static final int OP_MANAGE_EXTERNAL_STORAGE = 92;
+    /** @hide Communicate cross-profile within the same profile group. */
+    public static final int OP_INTERACT_ACROSS_PROFILES = 93;
 
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 93;
+    public static final int _NUM_OP = 94;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1143,6 +1146,10 @@
     public static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
             "android:manage_external_storage";
 
+    /** @hide Communicate cross-profile within the same profile group. */
+    @SystemApi
+    public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
+
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
@@ -1159,8 +1166,6 @@
     })
     private @interface ShouldCollectNoteOp {}
 
-    // Warning: If an permission is added here it also has to be added to
-    // com.android.packageinstaller.permission.utils.EventLogger
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
             // RUNTIME PERMISSIONS
             // Contacts
@@ -1222,6 +1227,7 @@
             OP_MANAGE_IPSEC_TUNNELS,
             OP_INSTANT_APP_START_FOREGROUND,
             OP_MANAGE_EXTERNAL_STORAGE,
+            OP_INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1326,6 +1332,7 @@
             OP_ACCESS_MEDIA_LOCATION,           // ACCESS_MEDIA_LOCATION
             OP_QUERY_ALL_PACKAGES,              // QUERY_ALL_PACKAGES
             OP_MANAGE_EXTERNAL_STORAGE,         // MANAGE_EXTERNAL_STORAGE
+            OP_INTERACT_ACROSS_PROFILES,        //INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1425,6 +1432,7 @@
             OPSTR_ACCESS_MEDIA_LOCATION,
             OPSTR_QUERY_ALL_PACKAGES,
             OPSTR_MANAGE_EXTERNAL_STORAGE,
+            OPSTR_INTERACT_ACROSS_PROFILES,
     };
 
     /**
@@ -1524,7 +1532,8 @@
             "READ_DEVICE_IDENTIFIERS",
             "ACCESS_MEDIA_LOCATION",
             "QUERY_ALL_PACKAGES",
-            "MANAGE_EXTERNAL_STORAGE"
+            "MANAGE_EXTERNAL_STORAGE",
+            "INTERACT_ACROSS_PROFILES"
     };
 
     /**
@@ -1626,6 +1635,7 @@
             Manifest.permission.ACCESS_MEDIA_LOCATION,
             null, // no permission for OP_QUERY_ALL_PACKAGES
             Manifest.permission.MANAGE_EXTERNAL_STORAGE,
+            android.Manifest.permission.INTERACT_ACROSS_PROFILES,
     };
 
     /**
@@ -1727,6 +1737,7 @@
             null, // ACCESS_MEDIA_LOCATION
             null, // QUERY_ALL_PACKAGES
             null, // MANAGE_EXTERNAL_STORAGE
+            null, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1827,6 +1838,7 @@
             false, // ACCESS_MEDIA_LOCATION
             false, // QUERY_ALL_PACKAGES
             false, // MANAGE_EXTERNAL_STORAGE
+            false, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1926,6 +1938,7 @@
             AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
             AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
             AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
+            AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -2029,6 +2042,7 @@
             false, // ACCESS_MEDIA_LOCATION
             false, // QUERY_ALL_PACKAGES
             false, // MANAGE_EXTERNAL_STORAGE
+            false, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -2046,7 +2060,7 @@
      * transaction. Not set if this thread is currently not executing a two way binder transaction.
      *
      * @see #startNotedAppOpsCollection
-     * @see #markAppOpNoted
+     * @see #getNotedOpCollectionMode
      */
     private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
 
@@ -2054,7 +2068,8 @@
      * If a thread is currently executing a two-way binder transaction, this stores the op-codes of
      * the app-ops that were noted during this transaction.
      *
-     * @see #markAppOpNoted
+     * @see #getNotedOpCollectionMode
+     * @see #collectNotedOpSync
      */
     private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
             new ThreadLocal<>();
@@ -2342,15 +2357,31 @@
      */
     @TestApi
     @SystemApi
-    @Immutable
-    @DataClass(genHiddenConstructor = true)
+    // @DataClass(genHiddenConstructor = true, genHiddenCopyConstructor = true)
+    // genHiddenCopyConstructor does not work for @hide @SystemApi classes
     public static final class OpEventProxyInfo implements Parcelable {
         /** UID of the proxy app that noted the op */
-        private final @IntRange(from = 0) int mUid;
+        private @IntRange(from = 0) int mUid;
         /** Package of the proxy that noted the op */
-        private final @Nullable String mPackageName;
+        private @Nullable String mPackageName;
         /** ID of the feature of the proxy that noted the op */
-        private final @Nullable String mFeatureId;
+        private @Nullable String mFeatureId;
+
+        /**
+         * Reinit existing object with new state.
+         *
+         * @param uid UID of the proxy app that noted the op
+         * @param packageName Package of the proxy that noted the op
+         * @param featureId ID of the feature of the proxy that noted the op
+         *
+         * @hide
+         */
+        public void reinit(@IntRange(from = 0) int uid, @Nullable String packageName,
+                @Nullable String featureId) {
+            mUid = Preconditions.checkArgumentNonnegative(uid);
+            mPackageName = packageName;
+            mFeatureId = featureId;
+        }
 
 
 
@@ -2394,6 +2425,18 @@
         }
 
         /**
+         * Copy constructor
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public OpEventProxyInfo(@NonNull OpEventProxyInfo orig) {
+            mUid = orig.mUid;
+            mPackageName = orig.mPackageName;
+            mFeatureId = orig.mFeatureId;
+        }
+
+        /**
          * UID of the proxy app that noted the op
          */
         @DataClass.Generated.Member
@@ -2472,14 +2515,15 @@
             }
         };
 
+        /*
         @DataClass.Generated(
-                time = 1576194071700L,
+                time = 1576814974615L,
                 codegenVersion = "1.0.14",
                 sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
-                inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.Nullable java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+                inputSignatures = "private @android.annotation.IntRange(from=0L) int mUid\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.Nullable java.lang.String mFeatureId\npublic  void reinit(int,java.lang.String,java.lang.String)\nclass OpEventProxyInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genHiddenCopyConstructor=true)")
         @Deprecated
         private void __metadata() {}
-
+        */
 
         //@formatter:on
         // End of generated code
@@ -2491,15 +2535,48 @@
      *
      * @hide
      */
-    @Immutable
     //@DataClass codegen verifier is broken
     public static final class NoteOpEvent implements Parcelable {
         /** Time of noteOp event */
-        public final @IntRange(from = 0) long noteTime;
+        private @IntRange(from = 0) long mNoteTime;
         /** The duration of this event (in case this is a startOp event, -1 otherwise). */
-        public final @IntRange(from = -1) long duration;
+        private @IntRange(from = -1) long mDuration;
         /** Proxy information of the noteOp event */
-        public final @Nullable OpEventProxyInfo proxy;
+        private @Nullable OpEventProxyInfo mProxy;
+
+        /**
+         * Reinit existing object with new state.
+         *
+         * @param noteTime Time of noteOp event
+         * @param duration The duration of this event (in case this is a startOp event,
+         *                 -1 otherwise).
+         * @param proxy Proxy information of the noteOp event
+         * @param proxyPool  The pool to release previous {@link OpEventProxyInfo} to
+         */
+        public void reinit(@IntRange(from = 0) long noteTime,
+                @IntRange(from = -1) long duration,
+                @Nullable OpEventProxyInfo proxy,
+                @NonNull Pools.Pool<OpEventProxyInfo> proxyPool) {
+            mNoteTime = Preconditions.checkArgumentNonnegative(noteTime);
+            mDuration = Preconditions.checkArgumentInRange(duration, -1L, Long.MAX_VALUE,
+                    "duration");
+
+            if (mProxy != null) {
+                proxyPool.release(mProxy);
+            }
+            mProxy = proxy;
+        }
+
+        /**
+         * Copy constructor
+         *
+         * @hide
+         */
+        public NoteOpEvent(@NonNull NoteOpEvent original) {
+            this(original.mNoteTime, original.mDuration,
+                    original.mProxy != null ? new OpEventProxyInfo(original.mProxy) : null);
+        }
+
 
 
         // Code below generated by codegen v1.0.14.
@@ -2530,19 +2607,43 @@
                 @IntRange(from = 0) long noteTime,
                 @IntRange(from = -1) long duration,
                 @Nullable OpEventProxyInfo proxy) {
-            this.noteTime = noteTime;
+            this.mNoteTime = noteTime;
             com.android.internal.util.AnnotationValidations.validate(
-                    IntRange.class, null, noteTime,
+                    IntRange.class, null, mNoteTime,
                     "from", 0);
-            this.duration = duration;
+            this.mDuration = duration;
             com.android.internal.util.AnnotationValidations.validate(
-                    IntRange.class, null, duration,
+                    IntRange.class, null, mDuration,
                     "from", -1);
-            this.proxy = proxy;
+            this.mProxy = proxy;
 
             // onConstructed(); // You can define this method to get a callback
         }
 
+        /**
+         * Time of noteOp event
+         */
+        @DataClass.Generated.Member
+        public @IntRange(from = 0) long getNoteTime() {
+            return mNoteTime;
+        }
+
+        /**
+         * The duration of this event (in case this is a startOp event, -1 otherwise).
+         */
+        @DataClass.Generated.Member
+        public @IntRange(from = -1) long getDuration() {
+            return mDuration;
+        }
+
+        /**
+         * Proxy information of the noteOp event
+         */
+        @DataClass.Generated.Member
+        public @Nullable OpEventProxyInfo getProxy() {
+            return mProxy;
+        }
+
         @Override
         @DataClass.Generated.Member
         public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -2550,11 +2651,11 @@
             // void parcelFieldName(Parcel dest, int flags) { ... }
 
             byte flg = 0;
-            if (proxy != null) flg |= 0x4;
+            if (mProxy != null) flg |= 0x4;
             dest.writeByte(flg);
-            dest.writeLong(noteTime);
-            dest.writeLong(duration);
-            if (proxy != null) dest.writeTypedObject(proxy, flags);
+            dest.writeLong(mNoteTime);
+            dest.writeLong(mDuration);
+            if (mProxy != null) dest.writeTypedObject(mProxy, flags);
         }
 
         @Override
@@ -2569,20 +2670,19 @@
             // static FieldType unparcelFieldName(Parcel in) { ... }
 
             byte flg = in.readByte();
-            long _noteTime = in.readLong();
-            long _duration = in.readLong();
-            OpEventProxyInfo _proxy = (flg & 0x4) == 0 ? null : (OpEventProxyInfo) in.readTypedObject(
-                    OpEventProxyInfo.CREATOR);
+            long noteTime = in.readLong();
+            long duration = in.readLong();
+            OpEventProxyInfo proxy = (flg & 0x4) == 0 ? null : (OpEventProxyInfo) in.readTypedObject(OpEventProxyInfo.CREATOR);
 
-            this.noteTime = _noteTime;
+            this.mNoteTime = noteTime;
             com.android.internal.util.AnnotationValidations.validate(
-                    IntRange.class, null, noteTime,
+                    IntRange.class, null, mNoteTime,
                     "from", 0);
-            this.duration = _duration;
+            this.mDuration = duration;
             com.android.internal.util.AnnotationValidations.validate(
-                    IntRange.class, null, duration,
+                    IntRange.class, null, mDuration,
                     "from", -1);
-            this.proxy = _proxy;
+            this.mProxy = proxy;
 
             // onConstructed(); // You can define this method to get a callback
         }
@@ -2603,10 +2703,10 @@
 
         /*
         @DataClass.Generated(
-                time = 1574809856220L,
+                time = 1576811792274L,
                 codegenVersion = "1.0.14",
                 sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
-                inputSignatures = "public final @android.annotation.IntRange(from=0L) long noteTime\npublic final @android.annotation.IntRange(from=-1) long duration\npublic final @android.annotation.Nullable android.app.NoteOpEventProxyInfo proxy\nclass NoteOpEvent extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
+                inputSignatures = "private @android.annotation.IntRange(from=0L) long mNoteTime\nprivate @android.annotation.IntRange(from=-1) long mDuration\nprivate @android.annotation.Nullable android.app.OpEventProxyInfo mProxy\npublic  void reinit(long,long,android.app.OpEventProxyInfo,android.util.Pools.Pool<android.app.OpEventProxyInfo>)\npublic @java.lang.Override java.lang.Object clone()\nclass NoteOpEvent extends java.lang.Object implements [android.os.Parcelable, java.lang.Cloneable]\n@com.android.internal.util.DataClass")
         @Deprecated
         private void __metadata() {}
          */
@@ -2752,7 +2852,7 @@
                 return -1;
             }
 
-            return lastEvent.noteTime;
+            return lastEvent.getNoteTime();
         }
 
         /**
@@ -2848,7 +2948,7 @@
                 return -1;
             }
 
-            return lastEvent.noteTime;
+            return lastEvent.getNoteTime();
         }
 
         /**
@@ -2923,7 +3023,7 @@
                 return -1;
             }
 
-            return lastEvent.duration;
+            return lastEvent.getDuration();
         }
 
         /**
@@ -3001,7 +3101,7 @@
                 return null;
             }
 
-            return lastEvent.proxy;
+            return lastEvent.getProxy();
         }
 
         private static class LongSparseArrayParceling implements
@@ -3305,7 +3405,7 @@
                         toUidState, flags);
 
                 if (lastAccessEvent == null || (lastFeatureAccessEvent != null
-                        && lastFeatureAccessEvent.noteTime > lastAccessEvent.noteTime)) {
+                        && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) {
                     lastAccessEvent = lastFeatureAccessEvent;
                 }
             }
@@ -3336,7 +3436,7 @@
                 return -1;
             }
 
-            return lastEvent.noteTime;
+            return lastEvent.getNoteTime();
         }
 
         /**
@@ -3419,7 +3519,7 @@
                         toUidState, flags);
 
                 if (lastAccessEvent == null || (lastFeatureAccessEvent != null
-                        && lastFeatureAccessEvent.noteTime > lastAccessEvent.noteTime)) {
+                        && lastFeatureAccessEvent.getNoteTime() > lastAccessEvent.getNoteTime())) {
                     lastAccessEvent = lastFeatureAccessEvent;
                 }
             }
@@ -3450,7 +3550,7 @@
                 return -1;
             }
 
-            return lastEvent.noteTime;
+            return lastEvent.getNoteTime();
         }
 
         /**
@@ -3545,7 +3645,7 @@
                 return -1;
             }
 
-            return lastEvent.duration;
+            return lastEvent.getDuration();
         }
 
         /**
@@ -3675,7 +3775,7 @@
                 return null;
             }
 
-            return lastEvent.proxy;
+            return lastEvent.getProxy();
         }
 
 
@@ -3817,10 +3917,53 @@
         void visitHistoricalOps(@NonNull HistoricalOps ops);
         void visitHistoricalUidOps(@NonNull HistoricalUidOps ops);
         void visitHistoricalPackageOps(@NonNull HistoricalPackageOps ops);
+        void visitHistoricalFeatureOps(@NonNull HistoricalFeatureOps ops);
         void visitHistoricalOp(@NonNull HistoricalOp ops);
     }
 
     /**
+     * Specifies what parameters to filter historical appop requests for
+     *
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "FILTER_BY_" }, value = {
+            FILTER_BY_UID,
+            FILTER_BY_PACKAGE_NAME,
+            FILTER_BY_FEATURE_ID,
+            FILTER_BY_OP_NAMES
+    })
+    public @interface HistoricalOpsRequestFilter {}
+
+    /**
+     * Filter historical appop request by uid.
+     *
+     * @hide
+     */
+    public static final int FILTER_BY_UID = 1<<0;
+
+    /**
+     * Filter historical appop request by package name.
+     *
+     * @hide
+     */
+    public static final int FILTER_BY_PACKAGE_NAME = 1<<1;
+
+    /**
+     * Filter historical appop request by feature id.
+     *
+     * @hide
+     */
+    public static final int FILTER_BY_FEATURE_ID = 1<<2;
+
+    /**
+     * Filter historical appop request by op names.
+     *
+     * @hide
+     */
+    public static final int FILTER_BY_OP_NAMES = 1<<3;
+
+    /**
      * Request for getting historical app op usage. The request acts
      * as a filtering criteria when querying historical op usage.
      *
@@ -3832,17 +3975,22 @@
     public static final class HistoricalOpsRequest {
         private final int mUid;
         private final @Nullable String mPackageName;
+        private final @Nullable String mFeatureId;
         private final @Nullable List<String> mOpNames;
+        private final @HistoricalOpsRequestFilter int mFilter;
         private final long mBeginTimeMillis;
         private final long mEndTimeMillis;
         private final @OpFlags int mFlags;
 
         private HistoricalOpsRequest(int uid, @Nullable String packageName,
-                @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
-                @OpFlags int flags) {
+                @Nullable String featureId, @Nullable List<String> opNames,
+                @HistoricalOpsRequestFilter int filter, long beginTimeMillis,
+                long endTimeMillis, @OpFlags int flags) {
             mUid = uid;
             mPackageName = packageName;
+            mFeatureId = featureId;
             mOpNames = opNames;
+            mFilter = filter;
             mBeginTimeMillis = beginTimeMillis;
             mEndTimeMillis = endTimeMillis;
             mFlags = flags;
@@ -3858,7 +4006,9 @@
         public static final class Builder {
             private int mUid = Process.INVALID_UID;
             private @Nullable String mPackageName;
+            private @Nullable String mFeatureId;
             private @Nullable List<String> mOpNames;
+            private @HistoricalOpsRequestFilter int mFilter;
             private final long mBeginTimeMillis;
             private final long mEndTimeMillis;
             private @OpFlags int mFlags = OP_FLAGS_ALL;
@@ -3891,6 +4041,13 @@
                 Preconditions.checkArgument(uid == Process.INVALID_UID || uid >= 0,
                         "uid must be " + Process.INVALID_UID + " or non negative");
                 mUid = uid;
+
+                if (uid == Process.INVALID_UID) {
+                    mFilter &= ~FILTER_BY_UID;
+                } else {
+                    mFilter |= FILTER_BY_UID;
+                }
+
                 return this;
             }
 
@@ -3902,6 +4059,26 @@
              */
             public @NonNull Builder setPackageName(@Nullable String packageName) {
                 mPackageName = packageName;
+
+                if (packageName == null) {
+                    mFilter &= ~FILTER_BY_PACKAGE_NAME;
+                } else {
+                    mFilter |= FILTER_BY_PACKAGE_NAME;
+                }
+
+                return this;
+            }
+
+            /**
+             * Sets the feature id to query for.
+             *
+             * @param featureId The id of the feature.
+             * @return This builder.
+             */
+            public @NonNull Builder setFeatureId(@Nullable String featureId) {
+                mFeatureId = featureId;
+                mFilter |= FILTER_BY_FEATURE_ID;
+
                 return this;
             }
 
@@ -3920,6 +4097,13 @@
                     }
                 }
                 mOpNames = opNames;
+
+                if (mOpNames == null) {
+                    mFilter &= ~FILTER_BY_OP_NAMES;
+                } else {
+                    mFilter |= FILTER_BY_OP_NAMES;
+                }
+
                 return this;
             }
 
@@ -3944,8 +4128,8 @@
              * @return a new {@link HistoricalOpsRequest}.
              */
             public @NonNull HistoricalOpsRequest build() {
-                return new HistoricalOpsRequest(mUid, mPackageName, mOpNames,
-                        mBeginTimeMillis, mEndTimeMillis, mFlags);
+                return new HistoricalOpsRequest(mUid, mPackageName, mFeatureId, mOpNames,
+                        mFilter, mBeginTimeMillis, mEndTimeMillis, mFlags);
             }
         }
     }
@@ -4102,15 +4286,18 @@
         /**
          * AppPermissionUsage the ops to leave only the data we filter for.
          *
-         * @param uid Uid to filter for or {@link android.os.Process#INCIDENTD_UID} for all.
-         * @param packageName Package to filter for or null for all.
-         * @param opNames Ops to filter for or null for all.
+         * @param uid Uid to filter for.
+         * @param packageName Package to filter for.
+         * @param featureId Package to filter for.
+         * @param opNames Ops to filter for.
+         * @param filter Which parameters to filter on.
          * @param beginTimeMillis The begin time to filter for or {@link Long#MIN_VALUE} for all.
          * @param endTimeMillis The end time to filter for or {@link Long#MAX_VALUE} for all.
          *
          * @hide
          */
-        public void filter(int uid, @Nullable String packageName, @Nullable String[] opNames,
+        public void filter(int uid, @Nullable String packageName, @Nullable String featureId,
+                @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
                 long beginTimeMillis, long endTimeMillis) {
             final long durationMillis = getDurationMillis();
             mBeginTimeMillis = Math.max(mBeginTimeMillis, beginTimeMillis);
@@ -4120,10 +4307,10 @@
             final int uidCount = getUidCount();
             for (int i = uidCount - 1; i >= 0; i--) {
                 final HistoricalUidOps uidOp = mHistoricalUidOps.valueAt(i);
-                if (uid != Process.INVALID_UID && uid != uidOp.getUid()) {
+                if ((filter & FILTER_BY_UID) != 0 && uid != uidOp.getUid()) {
                     mHistoricalUidOps.removeAt(i);
                 } else {
-                    uidOp.filter(packageName, opNames, scaleFactor);
+                    uidOp.filter(packageName, featureId, opNames, filter, scaleFactor);
                 }
             }
         }
@@ -4151,25 +4338,28 @@
         /** @hide */
         @TestApi
         public void increaseAccessCount(int opCode, int uid, @NonNull String packageName,
-                @UidState int uidState,  @OpFlags int flags, long increment) {
+                @Nullable String featureId, @UidState int uidState,  @OpFlags int flags,
+                long increment) {
             getOrCreateHistoricalUidOps(uid).increaseAccessCount(opCode,
-                    packageName, uidState, flags, increment);
+                    packageName, featureId, uidState, flags, increment);
         }
 
         /** @hide */
         @TestApi
         public void increaseRejectCount(int opCode, int uid, @NonNull String packageName,
-                @UidState int uidState, @OpFlags int flags, long increment) {
+                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                long increment) {
             getOrCreateHistoricalUidOps(uid).increaseRejectCount(opCode,
-                    packageName, uidState, flags, increment);
+                    packageName, featureId, uidState, flags, increment);
         }
 
         /** @hide */
         @TestApi
         public void increaseAccessDuration(int opCode, int uid, @NonNull String packageName,
-                @UidState int uidState, @OpFlags int flags, long increment) {
+                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                long increment) {
             getOrCreateHistoricalUidOps(uid).increaseAccessDuration(opCode,
-                    packageName, uidState, flags, increment);
+                    packageName, featureId, uidState, flags, increment);
         }
 
         /** @hide */
@@ -4449,15 +4639,17 @@
             }
         }
 
-        private void filter(@Nullable String packageName, @Nullable String[] opNames,
+        private void filter(@Nullable String packageName, @Nullable String featureId,
+                @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
                 double fractionToRemove) {
             final int packageCount = getPackageCount();
             for (int i = packageCount - 1; i >= 0; i--) {
                 final HistoricalPackageOps packageOps = getPackageOpsAt(i);
-                if (packageName != null && !packageName.equals(packageOps.getPackageName())) {
+                if ((filter & FILTER_BY_PACKAGE_NAME) != 0 && !packageName.equals(
+                        packageOps.getPackageName())) {
                     mHistoricalPackageOps.removeAt(i);
                 } else {
-                    packageOps.filter(opNames, fractionToRemove);
+                    packageOps.filter(featureId, opNames, filter, fractionToRemove);
                 }
             }
         }
@@ -4474,21 +4666,24 @@
         }
 
         private void increaseAccessCount(int opCode, @NonNull String packageName,
-                @UidState int uidState, @OpFlags int flags, long increment) {
+                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseAccessCount(
-                    opCode, uidState, flags, increment);
+                    opCode, featureId, uidState, flags, increment);
         }
 
         private void increaseRejectCount(int opCode, @NonNull String packageName,
-                @UidState int uidState,  @OpFlags int flags, long increment) {
+                @Nullable String featureId, @UidState int uidState,  @OpFlags int flags,
+                long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseRejectCount(
-                    opCode, uidState, flags, increment);
+                    opCode, featureId, uidState, flags, increment);
         }
 
         private void increaseAccessDuration(int opCode, @NonNull String packageName,
-                @UidState int uidState, @OpFlags int flags, long increment) {
+                @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+                long increment) {
             getOrCreateHistoricalPackageOps(packageName).increaseAccessDuration(
-                    opCode, uidState, flags, increment);
+                    opCode, featureId, uidState, flags, increment);
         }
 
         /**
@@ -4633,7 +4828,7 @@
     @SystemApi
     public static final class HistoricalPackageOps implements Parcelable {
         private final @NonNull String mPackageName;
-        private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
+        private @Nullable ArrayMap<String, HistoricalFeatureOps> mHistoricalFeatureOps;
 
         /** @hide */
         public HistoricalPackageOps(@NonNull String packageName) {
@@ -4642,6 +4837,339 @@
 
         private HistoricalPackageOps(@NonNull HistoricalPackageOps other) {
             mPackageName = other.mPackageName;
+            final int opCount = other.getFeatureCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalFeatureOps origOps = other.getFeatureOpsAt(i);
+                final HistoricalFeatureOps cloneOps = new HistoricalFeatureOps(origOps);
+                if (mHistoricalFeatureOps == null) {
+                    mHistoricalFeatureOps = new ArrayMap<>(opCount);
+                }
+                mHistoricalFeatureOps.put(cloneOps.getFeatureId(), cloneOps);
+            }
+        }
+
+        private HistoricalPackageOps(@NonNull Parcel parcel) {
+            mPackageName = parcel.readString();
+            mHistoricalFeatureOps = parcel.createTypedArrayMap(HistoricalFeatureOps.CREATOR);
+        }
+
+        private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
+            HistoricalPackageOps splice = null;
+            final int featureCount = getFeatureCount();
+            for (int i = 0; i < featureCount; i++) {
+                final HistoricalFeatureOps origOps = getFeatureOpsAt(i);
+                final HistoricalFeatureOps spliceOps = origOps.splice(fractionToRemove);
+                if (spliceOps != null) {
+                    if (splice == null) {
+                        splice = new HistoricalPackageOps(mPackageName);
+                    }
+                    if (splice.mHistoricalFeatureOps == null) {
+                        splice.mHistoricalFeatureOps = new ArrayMap<>();
+                    }
+                    splice.mHistoricalFeatureOps.put(spliceOps.getFeatureId(), spliceOps);
+                }
+            }
+            return splice;
+        }
+
+        private void merge(@NonNull HistoricalPackageOps other) {
+            final int featureCount = other.getFeatureCount();
+            for (int i = 0; i < featureCount; i++) {
+                final HistoricalFeatureOps otherFeatureOps = other.getFeatureOpsAt(i);
+                final HistoricalFeatureOps thisFeatureOps = getFeatureOps(
+                        otherFeatureOps.getFeatureId());
+                if (thisFeatureOps != null) {
+                    thisFeatureOps.merge(otherFeatureOps);
+                } else {
+                    if (mHistoricalFeatureOps == null) {
+                        mHistoricalFeatureOps = new ArrayMap<>();
+                    }
+                    mHistoricalFeatureOps.put(otherFeatureOps.getFeatureId(), otherFeatureOps);
+                }
+            }
+        }
+
+        private void filter(@Nullable String featureId, @Nullable String[] opNames,
+                @HistoricalOpsRequestFilter int filter, double fractionToRemove) {
+            final int featureCount = getFeatureCount();
+            for (int i = featureCount - 1; i >= 0; i--) {
+                final HistoricalFeatureOps featureOps = getFeatureOpsAt(i);
+                if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(featureId,
+                        featureOps.getFeatureId())) {
+                    mHistoricalFeatureOps.removeAt(i);
+                } else {
+                    featureOps.filter(opNames, filter, fractionToRemove);
+                }
+            }
+        }
+
+        private void accept(@NonNull HistoricalOpsVisitor visitor) {
+            visitor.visitHistoricalPackageOps(this);
+            final int featureCount = getFeatureCount();
+            for (int i = 0; i < featureCount; i++) {
+                getFeatureOpsAt(i).accept(visitor);
+            }
+        }
+
+        private boolean isEmpty() {
+            final int featureCount = getFeatureCount();
+            for (int i = featureCount - 1; i >= 0; i--) {
+                final HistoricalFeatureOps featureOps = mHistoricalFeatureOps.valueAt(i);
+                if (!featureOps.isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private void increaseAccessCount(int opCode, @Nullable String featureId,
+                @UidState int uidState, @OpFlags int flags, long increment) {
+            getOrCreateHistoricalFeatureOps(featureId).increaseAccessCount(
+                    opCode, uidState, flags, increment);
+        }
+
+        private void increaseRejectCount(int opCode, @Nullable String featureId,
+                @UidState int uidState, @OpFlags int flags, long increment) {
+            getOrCreateHistoricalFeatureOps(featureId).increaseRejectCount(
+                    opCode, uidState, flags, increment);
+        }
+
+        private void increaseAccessDuration(int opCode, @Nullable String featureId,
+                @UidState int uidState, @OpFlags int flags, long increment) {
+            getOrCreateHistoricalFeatureOps(featureId).increaseAccessDuration(
+                    opCode, uidState, flags, increment);
+        }
+
+        /**
+         * Gets the package name which the data represents.
+         *
+         * @return The package name which the data represents.
+         */
+        public @NonNull String getPackageName() {
+            return mPackageName;
+        }
+
+        private @NonNull HistoricalFeatureOps getOrCreateHistoricalFeatureOps(
+                @Nullable String featureId) {
+            if (mHistoricalFeatureOps == null) {
+                mHistoricalFeatureOps = new ArrayMap<>();
+            }
+            HistoricalFeatureOps historicalFeatureOp = mHistoricalFeatureOps.get(featureId);
+            if (historicalFeatureOp == null) {
+                historicalFeatureOp = new HistoricalFeatureOps(featureId);
+                mHistoricalFeatureOps.put(featureId, historicalFeatureOp);
+            }
+            return historicalFeatureOp;
+        }
+
+        /**
+         * Gets number historical app ops.
+         *
+         * @return The number historical app ops.
+         * @see #getOpAt(int)
+         */
+        public @IntRange(from = 0) int getOpCount() {
+            int numOps = 0;
+            int numFeatures = getFeatureCount();
+
+            for (int code = 0; code < _NUM_OP; code++) {
+                String opName = opToPublicName(code);
+
+                for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+                    if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+                        numOps++;
+                        break;
+                    }
+                }
+            }
+
+            return numOps;
+        }
+
+        /**
+         * Gets the historical op at a given index.
+         *
+         * <p>This combines the counts from all features.
+         *
+         * @param index The index to lookup.
+         * @return The op at the given index.
+         * @see #getOpCount()
+         */
+        public @NonNull HistoricalOp getOpAt(@IntRange(from = 0) int index) {
+            int numOpsFound = 0;
+            int numFeatures = getFeatureCount();
+
+            for (int code = 0; code < _NUM_OP; code++) {
+                String opName = opToPublicName(code);
+
+                for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+                    if (getFeatureOpsAt(featureNum).getOp(opName) != null) {
+                        if (numOpsFound == index) {
+                            return getOp(opName);
+                        } else {
+                            numOpsFound++;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            throw new IndexOutOfBoundsException();
+        }
+
+        /**
+         * Gets the historical entry for a given op name.
+         *
+         * <p>This combines the counts from all features.
+         *
+         * @param opName The op name.
+         * @return The historical entry for that op name.
+         */
+        public @Nullable HistoricalOp getOp(@NonNull String opName) {
+            if (mHistoricalFeatureOps == null) {
+                return null;
+            }
+
+            HistoricalOp combinedOp = null;
+            int numFeatures = getFeatureCount();
+            for (int i = 0; i < numFeatures; i++) {
+                HistoricalOp featureOp = getFeatureOpsAt(i).getOp(opName);
+                if (featureOp != null) {
+                    if (combinedOp == null) {
+                        combinedOp = new HistoricalOp(featureOp);
+                    } else {
+                        combinedOp.merge(featureOp);
+                    }
+                }
+            }
+
+            return combinedOp;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel parcel, int flags) {
+            parcel.writeString(mPackageName);
+            parcel.writeTypedArrayMap(mHistoricalFeatureOps, flags);
+        }
+
+        public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
+                new Creator<HistoricalPackageOps>() {
+            @Override
+            public @NonNull HistoricalPackageOps createFromParcel(@NonNull Parcel parcel) {
+                return new HistoricalPackageOps(parcel);
+            }
+
+            @Override
+            public @NonNull HistoricalPackageOps[] newArray(int size) {
+                return new HistoricalPackageOps[size];
+            }
+        };
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null || getClass() != obj.getClass()) {
+                return false;
+            }
+            final HistoricalPackageOps other = (HistoricalPackageOps) obj;
+            if (!mPackageName.equals(other.mPackageName)) {
+                return false;
+            }
+            if (mHistoricalFeatureOps == null) {
+                if (other.mHistoricalFeatureOps != null) {
+                    return false;
+                }
+            } else if (!mHistoricalFeatureOps.equals(other.mHistoricalFeatureOps)) {
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mPackageName != null ? mPackageName.hashCode() : 0;
+            result = 31 * result + (mHistoricalFeatureOps != null ? mHistoricalFeatureOps.hashCode()
+                    : 0);
+            return result;
+        }
+
+        /**
+         * Gets number of feature with historical ops.
+         *
+         * @return The number of feature with historical ops.
+         *
+         * @see #getFeatureOpsAt(int)
+         */
+        public @IntRange(from = 0) int getFeatureCount() {
+            if (mHistoricalFeatureOps == null) {
+                return 0;
+            }
+            return mHistoricalFeatureOps.size();
+        }
+
+        /**
+         * Gets the historical feature ops at a given index.
+         *
+         * @param index The index.
+         *
+         * @return The historical feature ops at the given index.
+         *
+         * @see #getFeatureCount()
+         */
+        public @NonNull HistoricalFeatureOps getFeatureOpsAt(@IntRange(from = 0) int index) {
+            if (mHistoricalFeatureOps == null) {
+                throw new IndexOutOfBoundsException();
+            }
+            return mHistoricalFeatureOps.valueAt(index);
+        }
+
+        /**
+         * Gets the historical feature ops for a given feature.
+         *
+         * @param featureId The feature id.
+         *
+         * @return The historical ops for the feature.
+         */
+        public @Nullable HistoricalFeatureOps getFeatureOps(@NonNull String featureId) {
+            if (mHistoricalFeatureOps == null) {
+                return null;
+            }
+            return mHistoricalFeatureOps.get(featureId);
+        }
+    }
+
+    /**
+     * This class represents historical app op information about a feature in a package.
+     *
+     * @hide
+     */
+    @TestApi
+    @SystemApi
+    /* codegen verifier cannot deal with nested class parameters
+    @DataClass(genHiddenConstructor = true,
+            genEqualsHashCode = true, genHiddenCopyConstructor = true) */
+    @DataClass.Suppress("getHistoricalOps")
+    public static final class HistoricalFeatureOps implements Parcelable {
+        /** Id of the {@link Context#createFeatureContext feature} in the package */
+        private final @Nullable String mFeatureId;
+
+        /** Ops for this feature */
+        private @Nullable ArrayMap<String, HistoricalOp> mHistoricalOps;
+
+        /** @hide */
+        public HistoricalFeatureOps(@NonNull String featureId) {
+            mFeatureId = featureId;
+        }
+
+        private HistoricalFeatureOps(@NonNull HistoricalFeatureOps other) {
+            mFeatureId = other.mFeatureId;
             final int opCount = other.getOpCount();
             for (int i = 0; i < opCount; i++) {
                 final HistoricalOp origOp = other.getOpAt(i);
@@ -4653,20 +5181,15 @@
             }
         }
 
-        private HistoricalPackageOps(@NonNull Parcel parcel) {
-            mPackageName = parcel.readString();
-            mHistoricalOps = parcel.createTypedArrayMap(HistoricalOp.CREATOR);
-        }
-
-        private @Nullable HistoricalPackageOps splice(double fractionToRemove) {
-            HistoricalPackageOps splice = null;
+        private @Nullable HistoricalFeatureOps splice(double fractionToRemove) {
+            HistoricalFeatureOps splice = null;
             final int opCount = getOpCount();
             for (int i = 0; i < opCount; i++) {
                 final HistoricalOp origOps = getOpAt(i);
                 final HistoricalOp spliceOps = origOps.splice(fractionToRemove);
                 if (spliceOps != null) {
                     if (splice == null) {
-                        splice = new HistoricalPackageOps(mPackageName);
+                        splice = new HistoricalFeatureOps(mFeatureId, null);
                     }
                     if (splice.mHistoricalOps == null) {
                         splice.mHistoricalOps = new ArrayMap<>();
@@ -4677,7 +5200,7 @@
             return splice;
         }
 
-        private void merge(@NonNull HistoricalPackageOps other) {
+        private void merge(@NonNull HistoricalFeatureOps other) {
             final int opCount = other.getOpCount();
             for (int i = 0; i < opCount; i++) {
                 final HistoricalOp otherOp = other.getOpAt(i);
@@ -4693,11 +5216,13 @@
             }
         }
 
-        private void filter(@Nullable String[] opNames, double scaleFactor) {
+        private void filter(@Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
+                double scaleFactor) {
             final int opCount = getOpCount();
             for (int i = opCount - 1; i >= 0; i--) {
                 final HistoricalOp op = mHistoricalOps.valueAt(i);
-                if (opNames != null && !ArrayUtils.contains(opNames, op.getOpName())) {
+                if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNames,
+                        op.getOpName())) {
                     mHistoricalOps.removeAt(i);
                 } else {
                     op.filter(scaleFactor);
@@ -4732,15 +5257,6 @@
         }
 
         /**
-         * Gets the package name which the data represents.
-         *
-         * @return The package name which the data represents.
-         */
-        public @NonNull String getPackageName() {
-            return mPackageName;
-        }
-
-        /**
          * Gets number historical app ops.
          *
          * @return The number historical app ops.
@@ -4780,19 +5296,8 @@
             return mHistoricalOps.get(opName);
         }
 
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(@NonNull Parcel parcel, int flags) {
-            parcel.writeString(mPackageName);
-            parcel.writeTypedArrayMap(mHistoricalOps, flags);
-        }
-
         private void accept(@NonNull HistoricalOpsVisitor visitor) {
-            visitor.visitHistoricalPackageOps(this);
+            visitor.visitHistoricalFeatureOps(this);
             final int opCount = getOpCount();
             for (int i = 0; i < opCount; i++) {
                 getOpAt(i).accept(visitor);
@@ -4812,47 +5317,143 @@
             return op;
         }
 
-        public static final @android.annotation.NonNull Creator<HistoricalPackageOps> CREATOR =
-                new Creator<HistoricalPackageOps>() {
+
+
+        // Code below generated by codegen v1.0.14.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AppOpsManager.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
+
+
+        /**
+         * Creates a new HistoricalFeatureOps.
+         *
+         * @param featureId
+         *   Id of the {@link Context#createFeatureContext feature} in the package
+         * @param historicalOps
+         *   Ops for this feature
+         * @hide
+         */
+        @DataClass.Generated.Member
+        public HistoricalFeatureOps(
+                @Nullable String featureId,
+                @Nullable ArrayMap<String,HistoricalOp> historicalOps) {
+            this.mFeatureId = featureId;
+            this.mHistoricalOps = historicalOps;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        /**
+         * Id of the {@link Context#createFeatureContext feature} in the package
+         */
+        @DataClass.Generated.Member
+        public @Nullable String getFeatureId() {
+            return mFeatureId;
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public boolean equals(@Nullable Object o) {
+            // You can override field equality logic by defining either of the methods like:
+            // boolean fieldNameEquals(HistoricalFeatureOps other) { ... }
+            // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            @SuppressWarnings("unchecked")
+            HistoricalFeatureOps that = (HistoricalFeatureOps) o;
+            //noinspection PointlessBooleanExpression
+            return true
+                    && Objects.equals(mFeatureId, that.mFeatureId)
+                    && Objects.equals(mHistoricalOps, that.mHistoricalOps);
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int hashCode() {
+            // You can override field hashCode logic by defining methods like:
+            // int fieldNameHashCode() { ... }
+
+            int _hash = 1;
+            _hash = 31 * _hash + Objects.hashCode(mFeatureId);
+            _hash = 31 * _hash + Objects.hashCode(mHistoricalOps);
+            return _hash;
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            // You can override field parcelling by defining methods like:
+            // void parcelFieldName(Parcel dest, int flags) { ... }
+
+            byte flg = 0;
+            if (mFeatureId != null) flg |= 0x1;
+            if (mHistoricalOps != null) flg |= 0x2;
+            dest.writeByte(flg);
+            if (mFeatureId != null) dest.writeString(mFeatureId);
+            if (mHistoricalOps != null) dest.writeMap(mHistoricalOps);
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int describeContents() { return 0; }
+
+        /** @hide */
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        @DataClass.Generated.Member
+        /* package-private */ HistoricalFeatureOps(@NonNull Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            byte flg = in.readByte();
+            String featureId = (flg & 0x1) == 0 ? null : in.readString();
+            ArrayMap<String,HistoricalOp> historicalOps = null;
+            if ((flg & 0x2) != 0) {
+                historicalOps = new ArrayMap();
+                in.readMap(historicalOps, HistoricalOp.class.getClassLoader());
+            }
+
+            this.mFeatureId = featureId;
+            this.mHistoricalOps = historicalOps;
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @DataClass.Generated.Member
+        public static final @NonNull Parcelable.Creator<HistoricalFeatureOps> CREATOR
+                = new Parcelable.Creator<HistoricalFeatureOps>() {
             @Override
-            public @NonNull HistoricalPackageOps createFromParcel(@NonNull Parcel parcel) {
-                return new HistoricalPackageOps(parcel);
+            public HistoricalFeatureOps[] newArray(int size) {
+                return new HistoricalFeatureOps[size];
             }
 
             @Override
-            public @NonNull HistoricalPackageOps[] newArray(int size) {
-                return new HistoricalPackageOps[size];
+            public HistoricalFeatureOps createFromParcel(@NonNull Parcel in) {
+                return new HistoricalFeatureOps(in);
             }
         };
 
-        @Override
-        public boolean equals(@Nullable Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null || getClass() != obj.getClass()) {
-                return false;
-            }
-            final HistoricalPackageOps other = (HistoricalPackageOps) obj;
-            if (!mPackageName.equals(other.mPackageName)) {
-                return false;
-            }
-            if (mHistoricalOps == null) {
-                if (other.mHistoricalOps != null) {
-                    return false;
-                }
-            } else if (!mHistoricalOps.equals(other.mHistoricalOps)) {
-                return false;
-            }
-            return true;
-        }
+        /*
+        @DataClass.Generated(
+                time = 1578113234821L,
+                codegenVersion = "1.0.14",
+                sourceFile = "frameworks/base/core/java/android/app/AppOpsManager.java",
+                inputSignatures = "private final @android.annotation.Nullable java.lang.String mFeatureId\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.app.HistoricalOp> mHistoricalOps\nprivate @android.annotation.Nullable android.app.HistoricalFeatureOps splice(double)\nprivate  void merge(android.app.HistoricalFeatureOps)\nprivate  void filter(java.lang.String[],int,double)\nprivate  boolean isEmpty()\nprivate  void increaseAccessCount(int,int,int,long)\nprivate  void increaseRejectCount(int,int,int,long)\nprivate  void increaseAccessDuration(int,int,int,long)\npublic @android.annotation.IntRange(from=0L) int getOpCount()\npublic @android.annotation.NonNull android.app.HistoricalOp getOpAt(int)\npublic @android.annotation.Nullable android.app.HistoricalOp getOp(java.lang.String)\nprivate  void accept(android.app.HistoricalOpsVisitor)\nprivate @android.annotation.NonNull android.app.HistoricalOp getOrCreateHistoricalOp(int)\nclass HistoricalFeatureOps extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genEqualsHashCode=true, genHiddenCopyConstructor=true)")
+        @Deprecated
+        private void __metadata() {}
+        */
 
-        @Override
-        public int hashCode() {
-            int result = mPackageName != null ? mPackageName.hashCode() : 0;
-            result = 31 * result + (mHistoricalOps != null ? mHistoricalOps.hashCode() : 0);
-            return result;
-        }
+        //@formatter:on
+        // End of generated code
+
     }
 
     /**
@@ -5188,13 +5789,13 @@
             if (mOp != other.mOp) {
                 return false;
             }
-            if (!Objects.equals(mAccessCount, other.mAccessCount)) {
+            if (!equalsLongSparseLongArray(mAccessCount, other.mAccessCount)) {
                 return false;
             }
-            if (!Objects.equals(mRejectCount, other.mRejectCount)) {
+            if (!equalsLongSparseLongArray(mRejectCount, other.mRejectCount)) {
                 return false;
             }
-            return Objects.equals(mAccessDuration, other.mAccessDuration);
+            return equalsLongSparseLongArray(mAccessDuration, other.mAccessDuration);
         }
 
         @Override
@@ -5518,12 +6119,12 @@
     @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
     public void getHistoricalOps(@NonNull HistoricalOpsRequest request,
             @NonNull Executor executor, @NonNull Consumer<HistoricalOps> callback) {
-        Preconditions.checkNotNull(executor, "executor cannot be null");
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(callback, "callback cannot be null");
         try {
-            mService.getHistoricalOps(request.mUid, request.mPackageName, request.mOpNames,
-                    request.mBeginTimeMillis, request.mEndTimeMillis, request.mFlags,
-                    new RemoteCallback((result) -> {
+            mService.getHistoricalOps(request.mUid, request.mPackageName, request.mFeatureId,
+                    request.mOpNames, request.mFilter, request.mBeginTimeMillis,
+                    request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
                 try {
@@ -5557,12 +6158,12 @@
     @RequiresPermission(Manifest.permission.MANAGE_APPOPS)
     public void getHistoricalOpsFromDiskRaw(@NonNull HistoricalOpsRequest request,
             @Nullable Executor executor, @NonNull Consumer<HistoricalOps> callback) {
-        Preconditions.checkNotNull(executor, "executor cannot be null");
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(callback, "callback cannot be null");
         try {
             mService.getHistoricalOpsFromDiskRaw(request.mUid, request.mPackageName,
-                    request.mOpNames, request.mBeginTimeMillis, request.mEndTimeMillis,
-                    request.mFlags, new RemoteCallback((result) -> {
+                    request.mFeatureId, request.mOpNames, request.mFilter, request.mBeginTimeMillis,
+                    request.mEndTimeMillis, request.mFlags, new RemoteCallback((result) -> {
                 final HistoricalOps ops = result.getParcelable(KEY_HISTORICAL_OPS);
                 final long identity = Binder.clearCallingIdentity();
                 try {
@@ -6237,9 +6838,23 @@
     public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
             @Nullable String featureId, @Nullable String message) {
         try {
-            int mode = mService.noteOperation(op, uid, packageName, featureId);
+            int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
+            if (collectionMode == COLLECT_ASYNC) {
+                if (message == null) {
+                    // Set stack trace as default message
+                    message = getFormattedStackTrace();
+                }
+            }
+
+            int mode = mService.noteOperation(op, uid, packageName, featureId,
+                    collectionMode == COLLECT_ASYNC, message);
+
             if (mode == MODE_ALLOWED) {
-                markAppOpNoted(uid, packageName, op, featureId, message);
+                if (collectionMode == COLLECT_SELF) {
+                    collectNotedOpForSelf(op, featureId);
+                } else if (collectionMode == COLLECT_SYNC) {
+                    collectNotedOpSync(op, featureId);
+                }
             }
 
             return mode;
@@ -6383,14 +6998,27 @@
         int myUid = Process.myUid();
 
         try {
+            int collectionMode = getNotedOpCollectionMode(proxiedUid, proxiedPackageName, op);
+            if (collectionMode == COLLECT_ASYNC) {
+                if (message == null) {
+                    // Set stack trace as default message
+                    message = getFormattedStackTrace();
+                }
+            }
+
             int mode = mService.noteProxyOperation(op, proxiedUid, proxiedPackageName,
                     proxiedFeatureId, myUid, mContext.getOpPackageName(),
-                    mContext.getFeatureId());
-            if (mode == MODE_ALLOWED
-                    // Only collect app-ops when the proxy is trusted
-                    && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid)
-                    == PackageManager.PERMISSION_GRANTED) {
-                markAppOpNoted(proxiedUid, proxiedPackageName, op, proxiedFeatureId, message);
+                    mContext.getFeatureId(), collectionMode == COLLECT_ASYNC, message);
+
+            if (mode == MODE_ALLOWED) {
+                if (collectionMode == COLLECT_SELF) {
+                    collectNotedOpForSelf(op, proxiedFeatureId);
+                } else if (collectionMode == COLLECT_SYNC
+                        // Only collect app-ops when the proxy is trusted
+                        && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1,
+                        myUid) == PackageManager.PERMISSION_GRANTED) {
+                    collectNotedOpSync(op, proxiedFeatureId);
+                }
             }
 
             return mode;
@@ -6680,10 +7308,23 @@
     public int startOpNoThrow(int op, int uid, @NonNull String packageName,
             boolean startIfModeDefault, @Nullable String featureId, @Nullable String message) {
         try {
+            int collectionMode = getNotedOpCollectionMode(uid, packageName, op);
+            if (collectionMode == COLLECT_ASYNC) {
+                if (message == null) {
+                    // Set stack trace as default message
+                    message = getFormattedStackTrace();
+                }
+            }
+
             int mode = mService.startOperation(getClientId(), op, uid, packageName,
-                    featureId, startIfModeDefault);
+                    featureId, startIfModeDefault, collectionMode == COLLECT_ASYNC, message);
+
             if (mode == MODE_ALLOWED) {
-                markAppOpNoted(uid, packageName, op, featureId, message);
+                if (collectionMode == COLLECT_SELF) {
+                    collectNotedOpForSelf(op, featureId);
+                } else if (collectionMode == COLLECT_SYNC) {
+                    collectNotedOpSync(op, featureId);
+                }
             }
 
             return mode;
@@ -6847,85 +7488,106 @@
     }
 
     /**
-     * Mark an app-op as noted
+     * Collect a noted op for the current process.
+     *
+     * @param op The noted op
+     * @param featureId The feature the op is noted for
      */
-    private void markAppOpNoted(int uid, @Nullable String packageName, int code,
-            @Nullable String featureId, @Nullable String message) {
+    private void collectNotedOpForSelf(int op, @Nullable String featureId) {
+        synchronized (sLock) {
+            if (sNotedAppOpsCollector != null) {
+                sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
+            }
+        }
+    }
+
+    /**
+     * Collect a noted op when inside of a two-way binder call.
+     *
+     * <p> Delivered to caller via {@link #prefixParcelWithAppOpsIfNeeded}
+     *
+     * @param op The noted op
+     * @param featureId The feature the op is noted for
+     */
+    private void collectNotedOpSync(int op, @Nullable String featureId) {
+        // If this is inside of a two-way binder call:
+        // We are inside of a two-way binder call. Delivered to caller via
+        // {@link #prefixParcelWithAppOpsIfNeeded}
+        ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
+        if (appOpsNoted == null) {
+            appOpsNoted = new ArrayMap<>(1);
+            sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
+        }
+
+        long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
+        if (appOpsNotedForFeature == null) {
+            appOpsNotedForFeature = new long[2];
+            appOpsNoted.put(featureId, appOpsNotedForFeature);
+        }
+
+        if (op < 64) {
+            appOpsNotedForFeature[0] |= 1L << op;
+        } else {
+            appOpsNotedForFeature[1] |= 1L << (op - 64);
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            DONT_COLLECT,
+            COLLECT_SELF,
+            COLLECT_SYNC,
+            COLLECT_ASYNC
+    })
+    private @interface NotedOpCollectionMode {}
+    private static final int DONT_COLLECT = 0;
+    private static final int COLLECT_SELF = 1;
+    private static final int COLLECT_SYNC = 2;
+    private static final int COLLECT_ASYNC = 3;
+
+    /**
+     * Mark an app-op as noted.
+     */
+    private @NotedOpCollectionMode int getNotedOpCollectionMode(int uid,
+            @Nullable String packageName, int op) {
         if (packageName == null) {
             packageName = "android";
         }
 
         // check it the appops needs to be collected and cache result
-        if (sAppOpsToNote[code] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
+        if (sAppOpsToNote[op] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
             boolean shouldCollectNotes;
             try {
-                shouldCollectNotes = mService.shouldCollectNotes(code);
+                shouldCollectNotes = mService.shouldCollectNotes(op);
             } catch (RemoteException e) {
-                return;
+                return DONT_COLLECT;
             }
 
             if (shouldCollectNotes) {
-                sAppOpsToNote[code] = SHOULD_COLLECT_NOTE_OP;
+                sAppOpsToNote[op] = SHOULD_COLLECT_NOTE_OP;
             } else {
-                sAppOpsToNote[code] = SHOULD_NOT_COLLECT_NOTE_OP;
+                sAppOpsToNote[op] = SHOULD_NOT_COLLECT_NOTE_OP;
             }
         }
 
-        if (sAppOpsToNote[code] != SHOULD_COLLECT_NOTE_OP) {
-            return;
+        if (sAppOpsToNote[op] != SHOULD_COLLECT_NOTE_OP) {
+            return DONT_COLLECT;
+        }
+
+        synchronized (sLock) {
+            if (uid == Process.myUid()
+                    && packageName.equals(ActivityThread.currentOpPackageName())) {
+                return COLLECT_SELF;
+            }
         }
 
         Integer binderUid = sBinderThreadCallingUid.get();
 
-        synchronized (sLock) {
-            if (sNotedAppOpsCollector != null && uid == Process.myUid() && packageName.equals(
-                    ActivityThread.currentOpPackageName())) {
-                // This app is noting an app-op for itself. Deliver immediately.
-                sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code, featureId));
-
-                return;
-            }
-        }
-
         if (binderUid != null && binderUid == uid) {
-            // If this is inside of a two-way binder call: Delivered to caller via
-            // {@link #prefixParcelWithAppOpsIfNeeded}
-            // We are inside of a two-way binder call. Delivered to caller via
-            // {@link #prefixParcelWithAppOpsIfNeeded}
-            ArrayMap<String, long[]> appOpsNoted = sAppOpsNotedInThisBinderTransaction.get();
-            if (appOpsNoted == null) {
-                appOpsNoted = new ArrayMap<>(1);
-                sAppOpsNotedInThisBinderTransaction.set(appOpsNoted);
-            }
-
-            long[] appOpsNotedForFeature = appOpsNoted.get(featureId);
-            if (appOpsNotedForFeature == null) {
-                appOpsNotedForFeature = new long[2];
-                appOpsNoted.put(featureId, appOpsNotedForFeature);
-            }
-
-            if (code < 64) {
-                appOpsNotedForFeature[0] |= 1L << code;
-            } else {
-                appOpsNotedForFeature[1] |= 1L << (code - 64);
-            }
+            return COLLECT_SYNC;
         } else {
-            // Cannot deliver the note synchronous: Hence send it to the system server to
-            // notify the noted process.
-            if (message == null) {
-                // Default message is a stack trace
-                message = getFormattedStackTrace();
-            }
-
-            long token = Binder.clearCallingIdentity();
-            try {
-                mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code,
-                        featureId, message);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            return COLLECT_ASYNC;
         }
     }
 
@@ -7063,7 +7725,7 @@
         private final IAppOpsAsyncNotedCallback mAsyncCb = new IAppOpsAsyncNotedCallback.Stub() {
             @Override
             public void opNoted(AsyncNotedAppOp op) {
-                Preconditions.checkNotNull(op);
+                Objects.requireNonNull(op);
 
                 getAsyncNotedExecutor().execute(() -> onAsyncNoted(op));
             }
@@ -7332,7 +7994,8 @@
                 final long key = makeKey(uidState, flag);
 
                 NoteOpEvent event = events.get(key);
-                if (lastEvent == null || event != null && event.noteTime > lastEvent.noteTime) {
+                if (lastEvent == null
+                        || event != null && event.getNoteTime() > lastEvent.getNoteTime()) {
                     lastEvent = event;
                 }
             }
@@ -7341,6 +8004,30 @@
         return lastEvent;
     }
 
+    private static boolean equalsLongSparseLongArray(@Nullable LongSparseLongArray a,
+            @Nullable LongSparseLongArray b) {
+        if (a == b) {
+            return true;
+        }
+
+        if (a == null || b == null) {
+            return false;
+        }
+
+        if (a.size() != b.size()) {
+            return false;
+        }
+
+        int numEntries = a.size();
+        for (int i = 0; i < numEntries; i++) {
+            if (a.keyAt(i) != b.keyAt(i) || a.valueAt(i) != b.valueAt(i)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
     private static void writeLongSparseLongArrayToParcel(
             @Nullable LongSparseLongArray array, @NonNull Parcel parcel) {
         if (array != null) {
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index bd7ef2a..099037c 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.util.SparseIntArray;
 
+import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.QuadFunction;
 
 /**
@@ -63,12 +64,16 @@
          * @param uid The UID for which to note.
          * @param packageName The package for which to note. {@code null} for system package.
          * @param featureId Id of the feature in the package
+         * @param shouldCollectAsyncNotedOp If an {@link AsyncNotedAppOp} should be collected
+         * @param message The message in the async noted op
          * @param superImpl The super implementation.
          * @return The app op note result.
          */
         int noteOperation(int code, int uid, @Nullable String packageName,
-                @Nullable String featureId,
-                @NonNull QuadFunction<Integer, Integer, String, String, Integer> superImpl);
+                @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+                @Nullable String message,
+                @NonNull HexFunction<Integer, Integer, String, String, Boolean, String, Integer>
+                        superImpl);
     }
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7f26565..1c6a561 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -100,7 +100,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.UserIcons;
 
 import dalvik.system.VMRuntime;
@@ -2149,7 +2148,7 @@
             } else if (vol.isPrimaryPhysical()) {
                 volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
             } else {
-                volumeUuid = Preconditions.checkNotNull(vol.fsUuid);
+                volumeUuid = Objects.requireNonNull(vol.fsUuid);
             }
 
             return mPM.movePackage(packageName, volumeUuid);
@@ -2259,7 +2258,7 @@
             } else if (vol.isPrimaryPhysical()) {
                 volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL;
             } else {
-                volumeUuid = Preconditions.checkNotNull(vol.fsUuid);
+                volumeUuid = Objects.requireNonNull(vol.fsUuid);
             }
 
             return mPM.movePrimaryStorage(volumeUuid);
@@ -2661,8 +2660,8 @@
     /** @hide */
     @Override
     public KeySet getKeySetByAlias(String packageName, String alias) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkNotNull(alias);
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(alias);
         try {
             return mPM.getKeySetByAlias(packageName, alias);
         } catch (RemoteException e) {
@@ -2673,7 +2672,7 @@
     /** @hide */
     @Override
     public KeySet getSigningKeySet(String packageName) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         try {
             return mPM.getSigningKeySet(packageName);
         } catch (RemoteException e) {
@@ -2684,8 +2683,8 @@
     /** @hide */
     @Override
     public boolean isSignedBy(String packageName, KeySet ks) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkNotNull(ks);
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(ks);
         try {
             return mPM.isPackageSignedByKeySet(packageName, ks);
         } catch (RemoteException e) {
@@ -2696,8 +2695,8 @@
     /** @hide */
     @Override
     public boolean isSignedByExactly(String packageName, KeySet ks) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkNotNull(ks);
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(ks);
         try {
             return mPM.isPackageSignedByKeySetExactly(packageName, ks);
         } catch (RemoteException e) {
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 0e1f921..d993ec1 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -45,12 +45,6 @@
     /** Uid that noted the op */
     private final @IntRange(from = 0) int mNotingUid;
 
-    /**
-     * Package that noted the op. {@code null} if the package name that noted the op could be not
-     * be determined (e.g. when the op is noted from native code).
-     */
-    private final @Nullable String mNotingPackageName;
-
     /** {@link android.content.Context#createFeatureContext Feature} in the app */
     private final @Nullable String mFeatureId;
 
@@ -69,7 +63,7 @@
 
 
 
-    // Code below generated by codegen v1.0.9.
+    // Code below generated by codegen v1.0.14.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -89,9 +83,6 @@
      *   Op that was noted
      * @param notingUid
      *   Uid that noted the op
-     * @param notingPackageName
-     *   Package that noted the op. {@code null} if the package name that noted the op could be not
-     *   be determined (e.g. when the op is noted from native code).
      * @param featureId
      *   {@link android.content.Context#createFeatureContext Feature} in the app
      * @param message
@@ -104,7 +95,6 @@
     public AsyncNotedAppOp(
             @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
             @IntRange(from = 0) int notingUid,
-            @Nullable String notingPackageName,
             @Nullable String featureId,
             @NonNull String message,
             @IntRange(from = 0) long time) {
@@ -117,7 +107,6 @@
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mNotingUid,
                 "from", 0);
-        this.mNotingPackageName = notingPackageName;
         this.mFeatureId = featureId;
         this.mMessage = message;
         com.android.internal.util.AnnotationValidations.validate(
@@ -139,15 +128,6 @@
     }
 
     /**
-     * Package that noted the op. {@code null} if the package name that noted the op could be not
-     * be determined (e.g. when the op is noted from native code).
-     */
-    @DataClass.Generated.Member
-    public @Nullable String getNotingPackageName() {
-        return mNotingPackageName;
-    }
-
-    /**
      * {@link android.content.Context#createFeatureContext Feature} in the app
      */
     @DataClass.Generated.Member
@@ -186,7 +166,6 @@
         return true
                 && mOpCode == that.mOpCode
                 && mNotingUid == that.mNotingUid
-                && java.util.Objects.equals(mNotingPackageName, that.mNotingPackageName)
                 && java.util.Objects.equals(mFeatureId, that.mFeatureId)
                 && java.util.Objects.equals(mMessage, that.mMessage)
                 && mTime == that.mTime;
@@ -201,7 +180,6 @@
         int _hash = 1;
         _hash = 31 * _hash + mOpCode;
         _hash = 31 * _hash + mNotingUid;
-        _hash = 31 * _hash + java.util.Objects.hashCode(mNotingPackageName);
         _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
         _hash = 31 * _hash + java.util.Objects.hashCode(mMessage);
         _hash = 31 * _hash + Long.hashCode(mTime);
@@ -215,12 +193,10 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mNotingPackageName != null) flg |= 0x4;
-        if (mFeatureId != null) flg |= 0x8;
+        if (mFeatureId != null) flg |= 0x4;
         dest.writeByte(flg);
         dest.writeInt(mOpCode);
         dest.writeInt(mNotingUid);
-        if (mNotingPackageName != null) dest.writeString(mNotingPackageName);
         if (mFeatureId != null) dest.writeString(mFeatureId);
         dest.writeString(mMessage);
         dest.writeLong(mTime);
@@ -240,8 +216,7 @@
         byte flg = in.readByte();
         int opCode = in.readInt();
         int notingUid = in.readInt();
-        String notingPackageName = (flg & 0x4) == 0 ? null : in.readString();
-        String featureId = (flg & 0x8) == 0 ? null : in.readString();
+        String featureId = (flg & 0x4) == 0 ? null : in.readString();
         String message = in.readString();
         long time = in.readLong();
 
@@ -254,7 +229,6 @@
         com.android.internal.util.AnnotationValidations.validate(
                 IntRange.class, null, mNotingUid,
                 "from", 0);
-        this.mNotingPackageName = notingPackageName;
         this.mFeatureId = featureId;
         this.mMessage = message;
         com.android.internal.util.AnnotationValidations.validate(
@@ -282,11 +256,15 @@
     };
 
     @DataClass.Generated(
-            time = 1571327470155L,
-            codegenVersion = "1.0.9",
+            time = 1578321462996L,
+            codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=92L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=93L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/app/AuthenticationRequiredException.java b/core/java/android/app/AuthenticationRequiredException.java
index 0d87336..847d2c4 100644
--- a/core/java/android/app/AuthenticationRequiredException.java
+++ b/core/java/android/app/AuthenticationRequiredException.java
@@ -21,7 +21,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 /**
  * Specialization of {@link SecurityException} that is thrown when authentication is needed from the
@@ -60,7 +60,7 @@
      */
     public AuthenticationRequiredException(Throwable cause, PendingIntent userAction) {
         super(cause.getMessage());
-        mUserAction = Preconditions.checkNotNull(userAction);
+        mUserAction = Objects.requireNonNull(userAction);
     }
 
     /**
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index bd87fcd..cd84310 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2794,7 +2794,7 @@
 
         public ApplicationContentResolver(Context context, ActivityThread mainThread) {
             super(context);
-            mMainThread = Preconditions.checkNotNull(mainThread);
+            mMainThread = Objects.requireNonNull(mainThread);
         }
 
         @Override
diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java
index ef3627b..0268f7c 100644
--- a/core/java/android/app/DirectAction.java
+++ b/core/java/android/app/DirectAction.java
@@ -172,7 +172,7 @@
          *     current application state.
          */
         public Builder(@NonNull String id) {
-            Preconditions.checkNotNull(id);
+            Objects.requireNonNull(id);
             mId = id;
         }
 
diff --git a/core/java/android/app/InstantAppResolverService.java b/core/java/android/app/InstantAppResolverService.java
index a413c60..0cd030e 100644
--- a/core/java/android/app/InstantAppResolverService.java
+++ b/core/java/android/app/InstantAppResolverService.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 
 import java.util.Arrays;
@@ -297,7 +298,10 @@
     public static final class InstantAppResolutionCallback {
         private final IRemoteCallback mCallback;
         private final int mSequence;
-        InstantAppResolutionCallback(int sequence, IRemoteCallback callback) {
+
+        /** @hide **/
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public InstantAppResolutionCallback(int sequence, IRemoteCallback callback) {
             mCallback = callback;
             mSequence = sequence;
         }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 453c600..775b1d1 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -46,7 +46,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.sysprop.ProductProperties;
+import android.sysprop.VndkProperties;
 import android.text.TextUtils;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
@@ -785,13 +785,12 @@
         // Similar to vendor apks, we should add /product/lib for apks from product partition
         // when product apps are marked as unbundled. We cannot use the same way from vendor
         // to check if lib path exists because there is possibility that /product/lib would not
-        // exist from legacy device while product apks are bundled. To make this clear, new
-        // property ("ro.product.apps.unbundled") is defined which should be true only when
-        // product apks are unbundled.
+        // exist from legacy device while product apks are bundled. To make this clear, we use
+        // "ro.product.vndk.version" property. If the property is defined, we regard all product
+        // apks as unbundled.
         if (mApplicationInfo.getCodePath() != null
-                && mApplicationInfo.isProduct() && ProductProperties.unbundled_apps().orElse(false)
-                // TODO(b/128557860): Change target SDK version when version code R is available.
-                && getTargetSdkVersion() == Build.VERSION_CODES.CUR_DEVELOPMENT) {
+                && mApplicationInfo.isProduct()
+                && VndkProperties.product_vndk_version().isPresent()) {
             isBundledApp = false;
         }
 
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index b9893aa..83d1de6 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -255,18 +255,17 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 if (pendingIntent != null) {
-                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
-                    IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    service.setBroadcastSubscriber(configKey, subscriberId, intentSender,
+                    service.setBroadcastSubscriber(configKey, subscriberId, pendingIntent,
                             mContext.getOpPackageName());
                 } else {
                     service.unsetBroadcastSubscriber(configKey, subscriberId,
                             mContext.getOpPackageName());
                 }
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+                Slog.e(TAG, "Failed to connect to statsmanager when adding broadcast subscriber",
+                        e);
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -309,18 +308,16 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 if (pendingIntent == null) {
                     service.removeDataFetchOperation(configKey, mContext.getOpPackageName());
                 } else {
-                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
-                    IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    service.setDataFetchOperation(configKey, intentSender,
+                    service.setDataFetchOperation(configKey, pendingIntent,
                             mContext.getOpPackageName());
                 }
 
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
+                Slog.e(TAG, "Failed to connect to statsmanager when registering data listener.");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -347,20 +344,18 @@
             throws StatsUnavailableException {
         synchronized (sLock) {
             try {
-                IStatsd service = getIStatsdLocked();
+                IStatsManagerService service = getIStatsManagerServiceLocked();
                 if (pendingIntent == null) {
                     service.removeActiveConfigsChangedOperation(mContext.getOpPackageName());
                     return new long[0];
                 } else {
-                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
-                    IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    return service.setActiveConfigsChangedOperation(intentSender,
+                    return service.setActiveConfigsChangedOperation(pendingIntent,
                             mContext.getOpPackageName());
                 }
 
             } catch (RemoteException e) {
-                Slog.e(TAG,
-                        "Failed to connect to statsd when registering active configs listener.");
+                Slog.e(TAG, "Failed to connect to statsmanager "
+                        + "when registering active configs listener.");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 31c73b9..ca3d0d7 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -198,6 +198,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Manages all of the system services that can be returned by {@link Context#getSystemService}.
@@ -1429,8 +1430,8 @@
             @NonNull StaticServiceProducerWithBinder<TServiceClass> serviceProducer) {
         ensureInitializing("registerStaticService");
         Preconditions.checkStringNotEmpty(serviceName);
-        Preconditions.checkNotNull(serviceWrapperClass);
-        Preconditions.checkNotNull(serviceProducer);
+        Objects.requireNonNull(serviceWrapperClass);
+        Objects.requireNonNull(serviceProducer);
 
         registerService(serviceName, serviceWrapperClass,
                 new StaticServiceFetcher<TServiceClass>() {
@@ -1453,8 +1454,8 @@
             @NonNull StaticServiceProducerWithoutBinder<TServiceClass> serviceProducer) {
         ensureInitializing("registerStaticService");
         Preconditions.checkStringNotEmpty(serviceName);
-        Preconditions.checkNotNull(serviceWrapperClass);
-        Preconditions.checkNotNull(serviceProducer);
+        Objects.requireNonNull(serviceWrapperClass);
+        Objects.requireNonNull(serviceProducer);
 
         registerService(serviceName, serviceWrapperClass,
                 new StaticServiceFetcher<TServiceClass>() {
@@ -1486,8 +1487,8 @@
             @NonNull ContextAwareServiceProducerWithBinder<TServiceClass> serviceProducer) {
         ensureInitializing("registerContextAwareService");
         Preconditions.checkStringNotEmpty(serviceName);
-        Preconditions.checkNotNull(serviceWrapperClass);
-        Preconditions.checkNotNull(serviceProducer);
+        Objects.requireNonNull(serviceWrapperClass);
+        Objects.requireNonNull(serviceProducer);
 
         registerService(serviceName, serviceWrapperClass,
                 new CachedServiceFetcher<TServiceClass>() {
@@ -1514,8 +1515,8 @@
             @NonNull ContextAwareServiceProducerWithoutBinder<TServiceClass> serviceProducer) {
         ensureInitializing("registerContextAwareService");
         Preconditions.checkStringNotEmpty(serviceName);
-        Preconditions.checkNotNull(serviceWrapperClass);
-        Preconditions.checkNotNull(serviceProducer);
+        Objects.requireNonNull(serviceWrapperClass);
+        Objects.requireNonNull(serviceProducer);
 
         registerService(serviceName, serviceWrapperClass,
                 new CachedServiceFetcher<TServiceClass>() {
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index c585b5f..7b45b72 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -48,6 +48,10 @@
                 }
             ],
             "file_patterns": ["INotificationManager\\.aidl"]
+        },
+        {
+            "name": "FrameworksInstantAppResolverTests",
+            "file_patterns": ["(/|^)InstantAppResolve[^/]*"]
         }
     ],
     "postsubmit": [
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
index b37120f..c262ec2 100644
--- a/core/java/android/app/VoiceInteractor.java
+++ b/core/java/android/app/VoiceInteractor.java
@@ -44,6 +44,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -1129,8 +1130,8 @@
      */
     public boolean registerOnDestroyedCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull Runnable callback) {
-        Preconditions.checkNotNull(executor);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
         if (isDestroyed()) {
             Log.w(TAG, "Cannot interact with a destroyed voice interactor");
             return false;
@@ -1146,7 +1147,7 @@
      * @return whether the callback was unregistered.
      */
     public boolean unregisterOnDestroyedCallback(@NonNull Runnable callback) {
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(callback);
         if (isDestroyed()) {
             Log.w(TAG, "Cannot interact with a destroyed voice interactor");
             return false;
diff --git a/core/java/android/app/admin/DevicePolicyEventLogger.java b/core/java/android/app/admin/DevicePolicyEventLogger.java
index 95a7973..4c0e176 100644
--- a/core/java/android/app/admin/DevicePolicyEventLogger.java
+++ b/core/java/android/app/admin/DevicePolicyEventLogger.java
@@ -25,6 +25,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * A wrapper for logging managed device events using {@link StatsLog}.
@@ -136,7 +137,7 @@
      * in that order.
      */
     public DevicePolicyEventLogger setStrings(String value, String[] values) {
-        Preconditions.checkNotNull(values, "values parameter cannot be null");
+        Objects.requireNonNull(values, "values parameter cannot be null");
         mStringArrayValue = new String[values.length + 1];
         mStringArrayValue[0] = value;
         System.arraycopy(values, 0, mStringArrayValue, 1, values.length);
@@ -150,7 +151,7 @@
      * and <code>values</code>, in that order.
      */
     public DevicePolicyEventLogger setStrings(String value1, String value2, String[] values) {
-        Preconditions.checkNotNull(values, "values parameter cannot be null");
+        Objects.requireNonNull(values, "values parameter cannot be null");
         mStringArrayValue = new String[values.length + 2];
         mStringArrayValue[0] = value1;
         mStringArrayValue[1] = value2;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index acdf919..f3028a1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -88,6 +88,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.Preconditions;
@@ -115,6 +116,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -4263,7 +4265,7 @@
      *            {@link #WIPE_SILENTLY} is set.
      */
     public void wipeData(int flags, @NonNull CharSequence reason) {
-        Preconditions.checkNotNull(reason, "reason string is null");
+        Objects.requireNonNull(reason, "reason string is null");
         Preconditions.checkStringNotEmpty(reason, "reason string is empty");
         Preconditions.checkArgument((flags & WIPE_SILENTLY) == 0, "WIPE_SILENTLY cannot be set");
         wipeDataInternal(flags, reason.toString());
@@ -8483,14 +8485,16 @@
     }
 
     /**
-     * Called by device owner to set the system wall clock time. This only takes effect if called
-     * when {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false} will be
-     * returned.
+     * Called by a device owner or a profile owner of an organization-owned managed
+     * profile to set the system wall clock time. This only takes effect if called when
+     * {@link android.provider.Settings.Global#AUTO_TIME} is 0, otherwise {@code false}
+     * will be returned.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with
      * @param millis time in milliseconds since the Epoch
      * @return {@code true} if set time succeeded, {@code false} otherwise.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device owner or a profile owner
+     * of an organization-owned managed profile.
      */
     public boolean setTime(@NonNull ComponentName admin, long millis) {
         throwIfParentInstance("setTime");
@@ -8505,16 +8509,18 @@
     }
 
     /**
-     * Called by device owner to set the system's persistent default time zone. This only takes
-     * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE} is 0, otherwise
-     * {@code false} will be returned.
+     * Called by a device owner or a profile owner of an organization-owned managed
+     * profile to set the system's persistent default time zone. This only takes
+     * effect if called when {@link android.provider.Settings.Global#AUTO_TIME_ZONE}
+     * is 0, otherwise {@code false} will be returned.
      *
      * @see android.app.AlarmManager#setTimeZone(String)
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with
      * @param timeZone one of the Olson ids from the list returned by
      *     {@link java.util.TimeZone#getAvailableIDs}
      * @return {@code true} if set timezone succeeded, {@code false} otherwise.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device owner or a profile owner
+     * of an organization-owned managed profile.
      */
     public boolean setTimeZone(@NonNull ComponentName admin, String timeZone) {
         throwIfParentInstance("setTimeZone");
@@ -10430,8 +10436,8 @@
             @NonNull String packageName, @NonNull @CallbackExecutor Executor executor,
             @NonNull OnClearApplicationUserDataListener listener) {
         throwIfParentInstance("clearAppData");
-        Preconditions.checkNotNull(executor);
-        Preconditions.checkNotNull(listener);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(listener);
         try {
             mService.clearApplicationUserData(admin, packageName,
                     new IPackageDataObserver.Stub() {
@@ -10881,7 +10887,7 @@
     @WorkerThread public @PrivateDnsModeErrorCodes int setGlobalPrivateDnsModeSpecifiedHost(
             @NonNull ComponentName admin, @NonNull String privateDnsHost) {
         throwIfParentInstance("setGlobalPrivateDnsModeSpecifiedHost");
-        Preconditions.checkNotNull(privateDnsHost, "dns resolver is null");
+        Objects.requireNonNull(privateDnsHost, "dns resolver is null");
 
         if (mService == null) {
             return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
@@ -11202,6 +11208,40 @@
     }
 
     /**
+     * Returns the combined set of the following:
+     * <ul>
+     * <li>The package names that the admin has previously set as allowed to request user consent
+     * for cross-profile communication, via {@link
+     * #setCrossProfilePackages(ComponentName, Set)}.</li>
+     * <li>The default package names set by the OEM that are allowed to request user consent for
+     * cross-profile communication without being explicitly enabled by the admin, via
+     * {@link R.array#cross_profile_apps}</li>
+     * </ul>
+     *
+     * @return the combined set of whitelisted package names set via
+     * {@link #setCrossProfilePackages(ComponentName, Set)} and
+     * {@link R.array#cross_profile_apps}
+     *
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            permission.INTERACT_ACROSS_USERS_FULL,
+            permission.INTERACT_ACROSS_USERS,
+            permission.INTERACT_ACROSS_PROFILES
+    })
+    public @NonNull Set<String> getAllCrossProfilePackages() {
+        throwIfParentInstance("getDefaultCrossProfilePackages");
+        if (mService != null) {
+            try {
+                return new ArraySet<>(mService.getAllCrossProfilePackages());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return Collections.emptySet();
+    }
+
+    /**
      * Returns whether the device is being used as a managed kiosk. These requirements are as
      * follows:
      * <ul>
@@ -11298,4 +11338,39 @@
         }
         return false;
     }
+
+    /**
+     * Called by Device owner to set packages as protected. User will not be able to clear app
+     * data or force-stop protected packages.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with
+     * @param packages The package names to protect.
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public void setProtectedPackages(@NonNull ComponentName admin, @NonNull List<String> packages) {
+        if (mService != null) {
+            try {
+                mService.setProtectedPackages(admin, packages);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Returns the list of packages protected by the device owner.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with
+     * @throws SecurityException if {@code admin} is not a device owner.
+     */
+    public @NonNull List<String> getProtectedPackages(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.getProtectedPackages(admin);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return Collections.emptyList();
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9c82ff6..55dfe2f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -444,10 +444,16 @@
     void setCrossProfilePackages(in ComponentName admin, in List<String> packageNames);
     List<String> getCrossProfilePackages(in ComponentName admin);
 
+    List<String> getAllCrossProfilePackages();
+
     boolean isManagedKiosk();
     boolean isUnattendedManagedKiosk();
 
     boolean startViewCalendarEventInManagedProfile(String packageName, long eventId, long start, long end, boolean allDay, int flags);
 
     boolean setKeyGrantForApp(in ComponentName admin, String callerPackage, String alias, String packageName, boolean hasGrant);
+
+    void setProtectedPackages(in ComponentName admin, in List<String> packages);
+
+    List<String> getProtectedPackages(in ComponentName admin);
 }
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index f0b87a8..91cf120 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -81,6 +81,7 @@
             TAG_CRYPTO_SELF_TEST_COMPLETED,
             TAG_KEY_INTEGRITY_VIOLATION,
             TAG_CERT_VALIDATION_FAILURE,
+            TAG_CAMERA_POLICY_SET
     })
     public @interface SecurityLogTag {}
 
@@ -433,6 +434,19 @@
             SecurityLogTags.SECURITY_CERT_VALIDATION_FAILURE;
 
     /**
+     * Indicates that the admin has set policy to disable camera.
+     * The log entry contains the following information about the event, encapsulated in an
+     * {@link Object} array and accessible via {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] whether the camera is disabled or not ({@code Integer}, 1 if it's disabled,
+     *      0 if enabled)
+     */
+    public static final int TAG_CAMERA_POLICY_SET =
+            SecurityLogTags.SECURITY_CAMERA_POLICY_SET;
+
+    /**
      * Event severity level indicating that the event corresponds to normal workflow.
      */
     public static final int LEVEL_INFO = 1;
@@ -561,6 +575,7 @@
                 case TAG_MAX_PASSWORD_ATTEMPTS_SET:
                 case TAG_USER_RESTRICTION_ADDED:
                 case TAG_USER_RESTRICTION_REMOVED:
+                case TAG_CAMERA_POLICY_SET:
                     return LEVEL_INFO;
                 case TAG_CERT_AUTHORITY_REMOVED:
                 case TAG_CRYPTO_SELF_TEST_COMPLETED:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index fe2519d..4e67fe2 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -38,3 +38,4 @@
 210031 security_crypto_self_test_completed      (success|1)
 210032 security_key_integrity_violation         (key_id|3),(uid|1)
 210033 security_cert_validation_failure         (reason|3)
+210034 security_camera_policy_set               (package|3),(admin_user|1),(target_user|1),(disabled|1)
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 80c5b17..22d8c87 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -41,6 +41,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * <p>This API automatically creates assist data from the platform's
@@ -1889,7 +1890,7 @@
 
         @Override
         public void setTextIdEntry(@NonNull String entryName) {
-            mNode.mTextIdEntry = Preconditions.checkNotNull(entryName);
+            mNode.mTextIdEntry = Objects.requireNonNull(entryName);
         }
 
         @Override
@@ -1899,7 +1900,7 @@
 
         @Override
         public void setHintIdEntry(@NonNull String entryName) {
-            mNode.mHintIdEntry = Preconditions.checkNotNull(entryName);
+            mNode.mHintIdEntry = Objects.requireNonNull(entryName);
         }
 
         @Override
diff --git a/core/java/android/app/prediction/AppPredictionManager.java b/core/java/android/app/prediction/AppPredictionManager.java
index cb5b7e7..ca22721 100644
--- a/core/java/android/app/prediction/AppPredictionManager.java
+++ b/core/java/android/app/prediction/AppPredictionManager.java
@@ -20,7 +20,7 @@
 import android.annotation.TestApi;
 import android.content.Context;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 /**
  * Class that provides methods to create prediction clients.
@@ -37,7 +37,7 @@
      * @hide
      */
     public AppPredictionManager(Context context) {
-        mContext = Preconditions.checkNotNull(context);
+        mContext = Objects.requireNonNull(context);
     }
 
     /**
diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java
index 6f21490..14e32b83 100644
--- a/core/java/android/app/prediction/AppTarget.java
+++ b/core/java/android/app/prediction/AppTarget.java
@@ -25,7 +25,7 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 /**
  * A representation of a launchable target.
@@ -55,9 +55,9 @@
         mId = id;
         mShortcutInfo = null;
 
-        mPackageName = Preconditions.checkNotNull(packageName);
+        mPackageName = Objects.requireNonNull(packageName);
         mClassName = className;
-        mUser = Preconditions.checkNotNull(user);
+        mUser = Objects.requireNonNull(user);
         mRank = 0;
     }
 
@@ -69,7 +69,7 @@
     public AppTarget(@NonNull AppTargetId id, @NonNull ShortcutInfo shortcutInfo,
             @Nullable String className) {
         mId = id;
-        mShortcutInfo = Preconditions.checkNotNull(shortcutInfo);
+        mShortcutInfo = Objects.requireNonNull(shortcutInfo);
 
         mPackageName = mShortcutInfo.getPackage();
         mUser = mShortcutInfo.getUserHandle();
@@ -224,9 +224,9 @@
         @TestApi
         public Builder(@NonNull AppTargetId id, @NonNull String packageName,
                 @NonNull UserHandle user) {
-            mId = Preconditions.checkNotNull(id);
-            mPackageName = Preconditions.checkNotNull(packageName);
-            mUser = Preconditions.checkNotNull(user);
+            mId = Objects.requireNonNull(id);
+            mPackageName = Objects.requireNonNull(packageName);
+            mUser = Objects.requireNonNull(user);
         }
 
         /**
@@ -237,8 +237,8 @@
         @SystemApi
         @TestApi
         public Builder(@NonNull AppTargetId id, @NonNull ShortcutInfo info) {
-            mId = Preconditions.checkNotNull(id);
-            mShortcutInfo = Preconditions.checkNotNull(info);
+            mId = Objects.requireNonNull(id);
+            mShortcutInfo = Objects.requireNonNull(info);
             mPackageName = info.getPackage();
             mUser = info.getUserHandle();
         }
@@ -253,8 +253,8 @@
             if (mPackageName != null) {
                 throw new IllegalArgumentException("Target is already set");
             }
-            mPackageName = Preconditions.checkNotNull(packageName);
-            mUser = Preconditions.checkNotNull(user);
+            mPackageName = Objects.requireNonNull(packageName);
+            mUser = Objects.requireNonNull(user);
             return this;
         }
 
@@ -266,7 +266,7 @@
         @Deprecated
         public Builder setTarget(@NonNull ShortcutInfo info) {
             setTarget(info.getPackage(), info.getUserHandle());
-            mShortcutInfo = Preconditions.checkNotNull(info);
+            mShortcutInfo = Objects.requireNonNull(info);
             return this;
         }
 
@@ -275,7 +275,7 @@
          */
         @NonNull
         public Builder setClassName(@NonNull String className) {
-            mClassName = Preconditions.checkNotNull(className);
+            mClassName = Objects.requireNonNull(className);
             return this;
         }
 
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 61eeacc..ea66fd47 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -630,9 +630,12 @@
      * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as required by
      * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}
      *
+     * @param userId The user ID to get the default SMS package for.
+     * @return the package name of the default SMS app, or {@code null} if not configured.
      * @hide
      */
     @Nullable
+    @SystemApi
     public String getDefaultSmsPackage(@UserIdInt int userId) {
         try {
             return mService.getDefaultSmsPackage(userId);
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index a7319f6..823fdd2 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -28,7 +28,6 @@
 import android.os.Parcelable;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -428,7 +427,7 @@
          * @see SliceItem#getSubType()
          */
         public Builder addSubSlice(@NonNull Slice slice, @Nullable @SliceSubtype String subType) {
-            Preconditions.checkNotNull(slice);
+            Objects.requireNonNull(slice);
             mItems.add(new SliceItem(slice, SliceItem.FORMAT_SLICE, subType,
                     slice.getHints().toArray(new String[slice.getHints().size()])));
             return this;
@@ -441,8 +440,8 @@
          */
         public Slice.Builder addAction(@NonNull PendingIntent action, @NonNull Slice s,
                 @Nullable @SliceSubtype String subType) {
-            Preconditions.checkNotNull(action);
-            Preconditions.checkNotNull(s);
+            Objects.requireNonNull(action);
+            Objects.requireNonNull(s);
             List<String> hints = s.getHints();
             s.mSpec = null;
             mItems.add(new SliceItem(action, s, SliceItem.FORMAT_ACTION, subType, hints.toArray(
@@ -468,7 +467,7 @@
          */
         public Builder addIcon(Icon icon, @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            Preconditions.checkNotNull(icon);
+            Objects.requireNonNull(icon);
             mItems.add(new SliceItem(icon, SliceItem.FORMAT_IMAGE, subType, hints));
             return this;
         }
@@ -481,7 +480,7 @@
         public Slice.Builder addRemoteInput(RemoteInput remoteInput,
                 @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            Preconditions.checkNotNull(remoteInput);
+            Objects.requireNonNull(remoteInput);
             mItems.add(new SliceItem(remoteInput, SliceItem.FORMAT_REMOTE_INPUT,
                     subType, hints));
             return this;
@@ -529,7 +528,7 @@
          */
         public Slice.Builder addBundle(Bundle bundle, @Nullable @SliceSubtype String subType,
                 @SliceHint List<String> hints) {
-            Preconditions.checkNotNull(bundle);
+            Objects.requireNonNull(bundle);
             mItems.add(new SliceItem(bundle, SliceItem.FORMAT_BUNDLE, subType,
                     hints));
             return this;
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 90ecce2..4da1acb 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -51,6 +51,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -241,7 +242,7 @@
      * @see Slice
      */
     public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> supportedSpecs) {
-        Preconditions.checkNotNull(uri, "uri");
+        Objects.requireNonNull(uri, "uri");
         ContentResolver resolver = mContext.getContentResolver();
         try (ContentProviderClient provider = resolver.acquireUnstableContentProviderClient(uri)) {
             if (provider == null) {
@@ -336,7 +337,7 @@
     }
 
     private Uri resolveStatic(@NonNull Intent intent, ContentResolver resolver) {
-        Preconditions.checkNotNull(intent, "intent");
+        Objects.requireNonNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
                 || intent.getData() != null,
                 "Slice intent must be explicit %s", intent);
@@ -371,7 +372,7 @@
      */
     public @Nullable Slice bindSlice(@NonNull Intent intent,
             @NonNull Set<SliceSpec> supportedSpecs) {
-        Preconditions.checkNotNull(intent, "intent");
+        Objects.requireNonNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
                 || intent.getData() != null,
                 "Slice intent must be explicit %s", intent);
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index 9877fc7..de8f470 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -17,6 +17,7 @@
 package android.app.timedetector;
 
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 
 /**
@@ -35,4 +36,5 @@
 interface ITimeDetectorService {
   void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion);
   void suggestManualTime(in ManualTimeSuggestion timeSuggestion);
+  void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion);
 }
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
similarity index 88%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
index 1fefda4..731c907 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.app.timedetector;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+parcelable NetworkTimeSuggestion;
diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
new file mode 100644
index 0000000..4c55ba1
--- /dev/null
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.TimestampedValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A time signal from a network time source like NTP. The value consists of the number of
+ * milliseconds elapsed since 1/1/1970 00:00:00 UTC and the time according to the elapsed realtime
+ * clock when that number was established. The elapsed realtime clock is considered accurate but
+ * volatile, so time signals must not be persisted across device resets.
+ *
+ * @hide
+ */
+public final class NetworkTimeSuggestion implements Parcelable {
+
+    public static final @NonNull Creator<NetworkTimeSuggestion> CREATOR =
+            new Creator<NetworkTimeSuggestion>() {
+                public NetworkTimeSuggestion createFromParcel(Parcel in) {
+                    return NetworkTimeSuggestion.createFromParcel(in);
+                }
+
+                public NetworkTimeSuggestion[] newArray(int size) {
+                    return new NetworkTimeSuggestion[size];
+                }
+            };
+
+    @NonNull
+    private final TimestampedValue<Long> mUtcTime;
+    @Nullable
+    private ArrayList<String> mDebugInfo;
+
+    public NetworkTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
+        mUtcTime = Objects.requireNonNull(utcTime);
+        Objects.requireNonNull(utcTime.getValue());
+    }
+
+    private static NetworkTimeSuggestion createFromParcel(Parcel in) {
+        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
+        NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(utcTime);
+        @SuppressWarnings("unchecked")
+        ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
+        suggestion.mDebugInfo = debugInfo;
+        return suggestion;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeParcelable(mUtcTime, 0);
+        dest.writeList(mDebugInfo);
+    }
+
+    @NonNull
+    public TimestampedValue<Long> getUtcTime() {
+        return mUtcTime;
+    }
+
+    @NonNull
+    public List<String> getDebugInfo() {
+        return mDebugInfo == null
+                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
+    }
+
+    /**
+     * Associates information with the instance that can be useful for debugging / logging. The
+     * information is present in {@link #toString()} but is not considered for
+     * {@link #equals(Object)} and {@link #hashCode()}.
+     */
+    public void addDebugInfo(String... debugInfos) {
+        if (mDebugInfo == null) {
+            mDebugInfo = new ArrayList<>();
+        }
+        mDebugInfo.addAll(Arrays.asList(debugInfos));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        NetworkTimeSuggestion that = (NetworkTimeSuggestion) o;
+        return Objects.equals(mUtcTime, that.mUtcTime);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUtcTime);
+    }
+
+    @Override
+    public String toString() {
+        return "NetworkTimeSuggestion{"
+                + "mUtcTime=" + mUtcTime
+                + ", mDebugInfo=" + mDebugInfo
+                + '}';
+    }
+}
diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java
index 48d5cd2..af9ece0 100644
--- a/core/java/android/app/timedetector/TimeDetector.java
+++ b/core/java/android/app/timedetector/TimeDetector.java
@@ -48,7 +48,7 @@
      * signal if better signals are available such as those that come from more reliable sources or
      * were determined more recently.
      */
-    @RequiresPermission(android.Manifest.permission.SET_TIME)
+    @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
     public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
         if (DEBUG) {
             Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion);
@@ -63,7 +63,7 @@
     /**
      * Suggests the user's manually entered current time to the detector.
      */
-    @RequiresPermission(android.Manifest.permission.SET_TIME)
+    @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
     public void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion) {
         if (DEBUG) {
             Log.d(TAG, "suggestManualTime called: " + timeSuggestion);
@@ -85,4 +85,19 @@
         manualTimeSuggestion.addDebugInfo(why);
         return manualTimeSuggestion;
     }
+
+    /**
+     * Suggests the time according to a network time source like NTP.
+     */
+    @RequiresPermission(android.Manifest.permission.SET_TIME)
+    public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
+        if (DEBUG) {
+            Log.d(TAG, "suggestNetworkTime called: " + timeSuggestion);
+        }
+        try {
+            mITimeDetectorService.suggestNetworkTime(timeSuggestion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 387a36b..e165d8a 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -47,7 +47,7 @@
      * detector may ignore the signal based on system settings, whether better information is
      * available, and so on.
      */
-    @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
+    @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE)
     public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
         if (DEBUG) {
             Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion);
@@ -63,7 +63,7 @@
      * Suggests the current time zone, determined for the user's manually information, to the
      * detector. The detector may ignore the signal based on system settings.
      */
-    @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
+    @RequiresPermission(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE)
     public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
         if (DEBUG) {
             Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion);
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 7412970..4346d97 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -16,8 +16,6 @@
 
 package android.app.usage;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -42,6 +40,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.Objects;
+
 /**
  * Provides access to network usage history and statistics. Usage data is collected in
  * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
@@ -418,7 +418,7 @@
     /** @hide */
     public void registerUsageCallback(NetworkTemplate template, int networkType,
             long thresholdBytes, UsageCallback callback, @Nullable Handler handler) {
-        checkNotNull(callback, "UsageCallback cannot be null");
+        Objects.requireNonNull(callback, "UsageCallback cannot be null");
 
         final Looper looper;
         if (handler == null) {
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index eecf092..1ef1563 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -63,8 +63,8 @@
 
     /** {@hide} */
     public StorageStatsManager(Context context, IStorageStatsManager service) {
-        mContext = Preconditions.checkNotNull(context);
-        mService = Preconditions.checkNotNull(service);
+        mContext = Objects.requireNonNull(context);
+        mService = Objects.requireNonNull(service);
     }
 
     /** {@hide} */
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 83eaa72..38498bc 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -472,63 +472,6 @@
     }
 
     /**
-     * Get the volume of the device.
-     *
-     * <p> The volume is between -128 dB (mute) to 0 dB.
-     *
-     * @return volume of the hearing aid device.
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getVolume() {
-        if (VDBG) {
-            log("getVolume()");
-        }
-        final IBluetoothHearingAid service = getService();
-        try {
-            if (service != null && isEnabled()) {
-                return service.getVolume();
-            }
-            if (service == null) Log.w(TAG, "Proxy not attached to service");
-            return 0;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-            return 0;
-        }
-    }
-
-    /**
-     * Tells remote device to adjust volume. Uses the following values:
-     * <ul>
-     * <li>{@link AudioManager#ADJUST_LOWER}</li>
-     * <li>{@link AudioManager#ADJUST_RAISE}</li>
-     * <li>{@link AudioManager#ADJUST_MUTE}</li>
-     * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
-     * </ul>
-     *
-     * @param direction One of the supported adjust values.
-     * @hide
-     */
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public void adjustVolume(int direction) {
-        if (DBG) log("adjustVolume(" + direction + ")");
-
-        final IBluetoothHearingAid service = getService();
-        try {
-            if (service == null) {
-                Log.w(TAG, "Proxy not attached to service");
-                return;
-            }
-
-            if (!isEnabled()) return;
-
-            service.adjustVolume(direction);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
-        }
-    }
-
-    /**
      * Tells remote device to set an absolute volume.
      *
      * @param volume Absolute volume to be set on remote
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 8f5cdf0..c1233b8 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -17,9 +17,12 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.os.Binder;
@@ -43,6 +46,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class BluetoothHidHost implements BluetoothProfile {
     private static final String TAG = "BluetoothHidHost";
     private static final boolean DBG = true;
@@ -66,6 +70,7 @@
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
      * receive.
      */
+    @SuppressLint("ActionValue")
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
@@ -325,7 +330,7 @@
      * {@inheritDoc}
      */
     @Override
-    public List<BluetoothDevice> getConnectedDevices() {
+    public @NonNull List<BluetoothDevice> getConnectedDevices() {
         if (VDBG) log("getConnectedDevices()");
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled()) {
@@ -342,6 +347,8 @@
 
     /**
      * {@inheritDoc}
+     *
+     * @hide
      */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
@@ -363,7 +370,7 @@
      * {@inheritDoc}
      */
     @Override
-    public int getConnectionState(BluetoothDevice device) {
+    public int getConnectionState(@Nullable BluetoothDevice device) {
         if (VDBG) log("getState(" + device + ")");
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -409,7 +416,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+    public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
@@ -457,7 +464,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    public int getConnectionPolicy(BluetoothDevice device) {
+    public int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothHidHost service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index c579fdf..948885e 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -16,7 +16,10 @@
 
 package android.bluetooth;
 
+import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -271,6 +274,42 @@
     }
 
     /**
+     * Pbap does not store connection policy, so this function only disconnects Pbap if
+     * connectionPolicy is CONNECTION_POLICY_FORBIDDEN.
+     *
+     * <p> The device should already be paired.
+     * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
+     * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+     *
+     * @param device Paired bluetooth device
+     * @param connectionPolicy is the connection policy to set to for this profile
+     * @return true if pbap is successfully disconnected, false otherwise
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
+            @ConnectionPolicy int connectionPolicy) {
+        if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+        try {
+            final IBluetoothPbap service = mService;
+            if (service != null && isEnabled()
+                    && isValidDevice(device)) {
+                if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
+                        && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) {
+                    return false;
+                }
+                return service.setConnectionPolicy(device, connectionPolicy);
+            }
+            if (service == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        }
+    }
+
+    /**
      * Disconnects the current Pbap client (PCE). Currently this call blocks,
      * it may soon be made asynchronous. Returns false if this proxy object is
      * not currently connected to the Pbap service.
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 27960b0..1967a01 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -305,6 +305,12 @@
         proto.end(token);
     }
 
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Two components are considered to be equal if the packages in which they reside have the
+     * same name, and if the classes that implement each component also have the same name.
+     */
     @Override
     public boolean equals(Object obj) {
         try {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4815d78..a28868e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3992,6 +3992,7 @@
      */
     public static final String NETWORK_STATS_SERVICE = "netstats";
     /** {@hide} */
+    @SystemApi
     public static final String NETWORK_POLICY_SERVICE = "netpolicy";
     /** {@hide} */
     public static final String NETWORK_WATCHLIST_SERVICE = "network_watchlist";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7967708..3bb0f92 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4224,9 +4224,10 @@
     public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
 
     /**
-     * Used for looking up a Data Loader Service providers.
+     * Used for looking up a Data Loader Service provider.
      * @hide
      */
+    @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f792127..b85c58a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2918,6 +2918,18 @@
     public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has
+     * the requisite hardware support to support reboot escrow of synthetic password for updates.
+     *
+     * <p>This feature implies that the device has the RebootEscrow HAL implementation.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow";
+
+    /**
      * Extra field name for the URI to a verification file. Passed to a package
      * verifier.
      *
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 47edf2e..32803ab 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -202,6 +202,7 @@
     public static final String TAG_OVERLAY = "overlay";
     public static final String TAG_PACKAGE = "package";
     public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
+    public static final String TAG_FEATURE = "feature";
     public static final String TAG_PERMISSION = "permission";
     public static final String TAG_PERMISSION_GROUP = "permission-group";
     public static final String TAG_PERMISSION_TREE = "permission-tree";
diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING
index df4ae09..0549c34 100644
--- a/core/java/android/content/pm/TEST_MAPPING
+++ b/core/java/android/content/pm/TEST_MAPPING
@@ -9,5 +9,11 @@
     {
       "path": "system/apex/tests"
     }
+  ],
+  "presubmit": [
+    {
+      "name": "FrameworksInstantAppResolverTests",
+      "file_patterns": ["(/|^)InstantApp[^/]*"]
+    }
   ]
 }
diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java
index 3e22eb2..21df7ec 100644
--- a/core/java/android/content/pm/VersionedPackage.java
+++ b/core/java/android/content/pm/VersionedPackage.java
@@ -96,6 +96,20 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        return o instanceof VersionedPackage
+                && ((VersionedPackage) o).mPackageName.equals(mPackageName)
+                && ((VersionedPackage) o).mVersionCode == mVersionCode;
+    }
+
+    @Override
+    public int hashCode() {
+        // Roll our own hash function without using Objects#hash which incurs the overhead
+        // of autoboxing.
+        return 31 * mPackageName.hashCode() + Long.hashCode(mVersionCode);
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index 35df474..990c835 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -27,6 +27,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
 import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
@@ -245,6 +246,9 @@
     List<ParsedInstrumentation> getInstrumentations();
 
     @Nullable
+    List<ParsedFeature> getFeatures();
+
+    @Nullable
     List<ParsedPermissionGroup> getPermissionGroups();
 
     @Nullable
@@ -403,6 +407,8 @@
 
     boolean isEnabled();
 
+    boolean isCrossProfile();
+
     boolean isEncryptionAware();
 
     boolean isExternal();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 7732316..a001ada 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -793,6 +793,10 @@
                     parseResult = parseKeySets(parseInput, parsingPackage, res, parser);
                     success = parseResult.isSuccess();
                     break;
+                case PackageParser.TAG_FEATURE:
+                    parseResult = parseFeature(parseInput, parsingPackage, res, parser);
+                    success = parseResult.isSuccess();
+                    break;
                 case PackageParser.TAG_PERMISSION_GROUP:
                     parseResult = parsePermissionGroup(parseInput, parsingPackage, res,
                             parser);
@@ -880,6 +884,13 @@
             );
         }
 
+        if (!ComponentParseUtils.ParsedFeature.isCombinationValid(parsingPackage.getFeatures())) {
+            return parseInput.error(
+                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                    "Combination <feature> tags are not valid"
+            );
+        }
+
         convertNewPermissions(parsingPackage);
 
         convertSplitPermissions(parsingPackage);
@@ -1260,6 +1271,31 @@
         return parseInput.success(parsingPackage);
     }
 
+    private static ParseResult parseFeature(
+            ParseInput parseInput,
+            ParsingPackage parsingPackage,
+            Resources res,
+            XmlResourceParser parser
+    ) throws IOException, XmlPullParserException {
+        // TODO(b/135203078): Remove, replace with ParseResult
+        String[] outError = new String[1];
+
+        ComponentParseUtils.ParsedFeature parsedFeature =
+                ComponentParseUtils.parseFeature(res, parser, outError);
+
+        if (parsedFeature == null || outError[0] != null) {
+            return parseInput.error(
+                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                    outError[0]
+            );
+        }
+
+        parsingPackage.addFeature(parsedFeature);
+
+        return parseInput.success(parsingPackage);
+    }
+
+
     private static ParseResult parsePermissionGroup(
             ParseInput parseInput,
             ParsingPackage parsingPackage,
@@ -2070,6 +2106,9 @@
                             sa.getBoolean(R.styleable.AndroidManifestApplication_enabled,
                                     true));
 
+            parsingPackage.setCrossProfile(
+                    sa.getBoolean(R.styleable.AndroidManifestApplication_crossProfile, false));
+
             parsingPackage.setIsGame(sa.getBoolean(
                     R.styleable.AndroidManifestApplication_isGame, false));
 
diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index fc210b2..5956857 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -24,6 +24,9 @@
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityTaskManager;
 import android.content.ComponentName;
@@ -47,6 +50,7 @@
 import android.os.Parcelable;
 import android.os.PatternMatcher;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
@@ -54,6 +58,7 @@
 import android.view.Gravity;
 
 import com.android.internal.R;
+import com.android.internal.util.DataClass;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -62,6 +67,7 @@
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
@@ -459,7 +465,8 @@
         }
 
         public void setPermission(String permission) {
-            this.permission = TextUtils.safeIntern(permission);
+            // Empty string must be converted to null
+            this.permission = TextUtils.isEmpty(permission) ? null : permission.intern();
         }
 
         public String getPermission() {
@@ -836,7 +843,9 @@
         }
 
         public void setReadPermission(String readPermission) {
-            this.readPermission = TextUtils.safeIntern(readPermission);
+            // Empty string must be converted to null
+            this.readPermission = TextUtils.isEmpty(readPermission)
+                    ? null : readPermission.intern();
         }
 
         public String getReadPermission() {
@@ -844,7 +853,9 @@
         }
 
         public void setWritePermission(String writePermission) {
-            this.writePermission = TextUtils.safeIntern(writePermission);
+            // Empty string must be converted to null
+            this.writePermission = TextUtils.isEmpty(writePermission)
+                    ? null : writePermission.intern();
         }
 
         public String getWritePermission() {
@@ -931,6 +942,186 @@
         };
     }
 
+    /**
+     * A {@link android.R.styleable#AndroidManifestFeature &lt;feature&gt;} tag parsed from the
+     * manifest.
+     */
+    // @DataClass verifier is broken, hence comment out for now
+    public static class ParsedFeature implements Parcelable {
+        /** Maximum length of featureId */
+        public static final int MAX_FEATURE_ID_LEN = 50;
+
+        /** Maximum amount of features per package */
+        private static final int MAX_NUM_FEATURES = 1000;
+
+        /** Id of the feature */
+        public final @NonNull String id;
+
+        /** User visible label fo the feature */
+        public final @StringRes int label;
+
+        /** Ids of previously declared features this feature inherits from */
+        public final @NonNull List<String> inheritFrom;
+
+        /**
+         * @return Is this set of features a valid combination for a single package?
+         */
+        public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
+            if (features == null) {
+                return true;
+            }
+
+            ArraySet<String> featureIds = new ArraySet<>(features.size());
+            ArraySet<String> inheritFromFeatureIds = new ArraySet<>();
+
+            int numFeatures = features.size();
+            if (numFeatures > MAX_NUM_FEATURES) {
+                return false;
+            }
+
+            for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+                boolean wasAdded = featureIds.add(features.get(featureNum).id);
+                if (!wasAdded) {
+                    // feature id is not unique
+                    return false;
+                }
+            }
+
+            for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+                ParsedFeature feature = features.get(featureNum);
+
+                int numInheritFrom = feature.inheritFrom.size();
+                for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
+                    String inheritFrom = feature.inheritFrom.get(inheritFromNum);
+
+                    if (featureIds.contains(inheritFrom)) {
+                        // Cannot inherit from a feature that is still defined
+                        return false;
+                    }
+
+                    boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
+                    if (!wasAdded) {
+                        // inheritFrom is not unique
+                        return false;
+                    }
+                }
+            }
+
+            return true;
+        }
+
+
+
+        // Code below generated by codegen v1.0.14.
+        //
+        // DO NOT MODIFY!
+        // CHECKSTYLE:OFF Generated code
+        //
+        // To regenerate run:
+        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
+
+
+        /**
+         * Creates a new ParsedFeature.
+         *
+         * @param id
+         *   Id of the feature
+         * @param label
+         *   User visible label fo the feature (if defined as resource)
+         * @param inheritFrom
+         *   Ids of previously declared features this feature inherits from
+         */
+        @DataClass.Generated.Member
+        public ParsedFeature(
+                @NonNull String id,
+                @StringRes int label,
+                @NonNull List<String> inheritFrom) {
+            this.id = id;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, id);
+            this.label = label;
+            com.android.internal.util.AnnotationValidations.validate(
+                    StringRes.class, null, label);
+            this.inheritFrom = inheritFrom;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, inheritFrom);
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            // You can override field parcelling by defining methods like:
+            // void parcelFieldName(Parcel dest, int flags) { ... }
+
+            dest.writeString(id);
+            dest.writeInt(label);
+            dest.writeStringList(inheritFrom);
+        }
+
+        @Override
+        @DataClass.Generated.Member
+        public int describeContents() { return 0; }
+
+        /** @hide */
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        @DataClass.Generated.Member
+        protected ParsedFeature(@NonNull Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            String _id = in.readString();
+            int _label = in.readInt();
+            List<String> _inheritFrom = new ArrayList<>();
+            in.readStringList(_inheritFrom);
+
+            this.id = _id;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, id);
+            this.label = _label;
+            com.android.internal.util.AnnotationValidations.validate(
+                    StringRes.class, null, label);
+            this.inheritFrom = _inheritFrom;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, inheritFrom);
+
+            // onConstructed(); // You can define this method to get a callback
+        }
+
+        @DataClass.Generated.Member
+        public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
+                = new Parcelable.Creator<ParsedFeature>() {
+            @Override
+            public ParsedFeature[] newArray(int size) {
+                return new ParsedFeature[size];
+            }
+
+            @Override
+            public ParsedFeature createFromParcel(@NonNull Parcel in) {
+                return new ParsedFeature(in);
+            }
+        };
+
+        /*@DataClass.Generated(
+                time = 1576783172965L,
+                codegenVersion = "1.0.14",
+                sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java",
+                inputSignatures = "public final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
+         */
+        @Deprecated
+        private void __metadata() {}
+
+
+        //@formatter:on
+        // End of generated code
+
+    }
+
     public static class ParsedPermission extends ParsedComponent<ParsedIntentInfo> {
 
         public String backgroundPermission;
@@ -2566,6 +2757,85 @@
         return result;
     }
 
+    public static ParsedFeature parseFeature(
+            Resources res,
+            XmlResourceParser parser,
+            String[] outError
+    ) throws IOException, XmlPullParserException {
+        String featureId;
+        int label;
+        List<String> inheritFrom = null;
+
+        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
+        if (sa == null) {
+            outError[0] = "<feature> could not be parsed";
+            return null;
+        }
+
+        try {
+            featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
+                    0);
+            if (featureId == null) {
+                outError[0] = "<featureId> does not specify android:featureId";
+                return null;
+            }
+            if (featureId.length() > ParsedFeature.MAX_FEATURE_ID_LEN) {
+                outError[0] = "<featureId> is too long. Max length is "
+                        + ParsedFeature.MAX_FEATURE_ID_LEN;
+                return null;
+            }
+
+            label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
+            if (label == Resources.ID_NULL) {
+                outError[0] = "<featureId> does not specify android:label";
+                return null;
+            }
+        } finally {
+            sa.recycle();
+        }
+
+        int type;
+        final int innerDepth = parser.getDepth();
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("inherit-from")) {
+                sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
+                if (sa == null) {
+                    outError[0] = "<inherit-from> could not be parsed";
+                    return null;
+                }
+
+                try {
+                    String inheritFromId = sa.getNonConfigurationString(
+                            R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);
+
+                    if (inheritFrom == null) {
+                        inheritFrom = new ArrayList<>();
+                    }
+                    inheritFrom.add(inheritFromId);
+                } finally {
+                    sa.recycle();
+                }
+            } else {
+                outError[0] = "Bad element under <feature>: " + tagName;
+                return null;
+            }
+        }
+
+        if (inheritFrom == null) {
+            inheritFrom = Collections.emptyList();
+        } else {
+            ((ArrayList) inheritFrom).trimToSize();
+        }
+
+        return new ParsedFeature(featureId, label, inheritFrom);
+    }
+
     public static ParsedPermission parsePermission(
             ParsingPackage parsingPackage,
             Resources res,
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 0e736d5..8677fce 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -36,6 +36,7 @@
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
 import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
 import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
@@ -175,6 +176,9 @@
     private ArrayList<ComponentParseUtils.ParsedProvider> providers;
 
     @Nullable
+    private ArrayList<ComponentParseUtils.ParsedFeature> features;
+
+    @Nullable
     private ArrayList<ComponentParseUtils.ParsedPermission> permissions;
 
     @Nullable
@@ -240,6 +244,7 @@
     private int descriptionRes;
     private String deviceProtectedDataDir;
     private boolean enabled;
+    private boolean crossProfile;
     private int flags;
     private int fullBackupContent;
     private boolean hiddenUntilInstalled;
@@ -579,6 +584,12 @@
         return permissions;
     }
 
+    @Nullable
+    @Override
+    public List<ParsedFeature> getFeatures() {
+        return features;
+    }
+
     @Override
     public String getCpuAbiOverride() {
         return cpuAbiOverride;
@@ -791,6 +802,12 @@
     }
 
     @Override
+    public PackageImpl addFeature(ParsedFeature feature) {
+        this.features = ArrayUtils.add(this.features, feature);
+        return this;
+    }
+
+    @Override
     public PackageImpl addPermission(ParsedPermission permission) {
         this.permissions = ArrayUtils.add(this.permissions, permission);
         return this;
@@ -1636,6 +1653,12 @@
     }
 
     @Override
+    public PackageImpl setCrossProfile(boolean crossProfile) {
+        this.crossProfile = crossProfile;
+        return this;
+    }
+
+    @Override
     public PackageImpl setUiOptions(int uiOptions) {
         this.uiOptions = uiOptions;
         return this;
@@ -2835,6 +2858,11 @@
     }
 
     @Override
+    public boolean isCrossProfile() {
+        return crossProfile;
+    }
+
+    @Override
     public String getManageSpaceActivityName() {
         return manageSpaceActivityName;
     }
@@ -3009,6 +3037,7 @@
         dest.writeTypedList(this.receivers);
         dest.writeTypedList(this.services);
         dest.writeTypedList(this.providers);
+        dest.writeTypedList(this.features);
         dest.writeTypedList(this.permissions);
         dest.writeTypedList(this.permissionGroups);
         dest.writeTypedList(this.instrumentations);
@@ -3042,6 +3071,7 @@
         dest.writeInt(this.descriptionRes);
         dest.writeString(this.deviceProtectedDataDir);
         dest.writeBoolean(this.enabled);
+        dest.writeBoolean(this.crossProfile);
         dest.writeInt(this.flags);
         dest.writeInt(this.fullBackupContent);
         dest.writeBoolean(this.hiddenUntilInstalled);
@@ -3160,6 +3190,7 @@
         this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR);
         this.services = in.createTypedArrayList(ParsedService.CREATOR);
         this.providers = in.createTypedArrayList(ParsedProvider.CREATOR);
+        this.features = in.createTypedArrayList(ParsedFeature.CREATOR);
         this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR);
         this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR);
         this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR);
@@ -3194,6 +3225,7 @@
         this.descriptionRes = in.readInt();
         this.deviceProtectedDataDir = in.readString();
         this.enabled = in.readBoolean();
+        this.crossProfile = in.readBoolean();
         this.flags = in.readInt();
         this.fullBackupContent = in.readInt();
         this.hiddenUntilInstalled = in.readBoolean();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index aff1b2e..411c749 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -24,6 +24,7 @@
 import android.content.pm.PackageParser;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
+import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
 import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
 import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
@@ -64,6 +65,8 @@
 
     ParsingPackage addOverlayable(String overlayableName, String actorName);
 
+    ParsingPackage addFeature(ParsedFeature permission);
+
     ParsingPackage addPermission(ParsedPermission permission);
 
     ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup);
@@ -229,6 +232,8 @@
 
     ParsingPackage setEnabled(boolean enabled);
 
+    ParsingPackage setCrossProfile(boolean crossProfile);
+
     ParsingPackage setFullBackupContent(int fullBackupContent);
 
     ParsingPackage setHasDomainUrls(boolean hasDomainUrls);
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index c89796d..6378db0 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -88,6 +89,11 @@
     private final SparseLongArray mCeSnapshotInodes;
 
     /**
+     * The userdata policy to execute when a rollback for this package is committed.
+     */
+    private final int mRollbackDataPolicy;
+
+    /**
      * Returns the name of the package to roll back from.
      */
     @NonNull
@@ -148,6 +154,11 @@
     }
 
     /** @hide */
+    public @PackageManager.RollbackDataPolicy int getRollbackDataPolicy() {
+        return mRollbackDataPolicy;
+    }
+
+    /** @hide */
     public IntArray getSnapshottedUsers() {
         return mSnapshottedUsers;
     }
@@ -181,11 +192,23 @@
             @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
             boolean isApex, @NonNull IntArray snapshottedUsers,
             @NonNull SparseLongArray ceSnapshotInodes) {
+        this(packageRolledBackFrom, packageRolledBackTo, pendingBackups, pendingRestores, isApex,
+                snapshottedUsers, ceSnapshotInodes, PackageManager.RollbackDataPolicy.RESTORE);
+    }
+
+    /** @hide */
+    public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
+            VersionedPackage packageRolledBackTo,
+            @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
+            boolean isApex, @NonNull IntArray snapshottedUsers,
+            @NonNull SparseLongArray ceSnapshotInodes,
+            @PackageManager.RollbackDataPolicy int rollbackDataPolicy) {
         this.mVersionRolledBackFrom = packageRolledBackFrom;
         this.mVersionRolledBackTo = packageRolledBackTo;
         this.mPendingBackups = pendingBackups;
         this.mPendingRestores = pendingRestores;
         this.mIsApex = isApex;
+        this.mRollbackDataPolicy = rollbackDataPolicy;
         this.mSnapshottedUsers = snapshottedUsers;
         this.mCeSnapshotInodes = ceSnapshotInodes;
     }
@@ -198,6 +221,7 @@
         this.mPendingBackups = null;
         this.mSnapshottedUsers = null;
         this.mCeSnapshotInodes = null;
+        this.mRollbackDataPolicy = PackageManager.RollbackDataPolicy.RESTORE;
     }
 
     @Override
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index cc5d3b1..3effc5a 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -17,20 +17,19 @@
 package android.database;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 
 /**
@@ -416,8 +415,8 @@
 
     @Override
     public void setNotificationUris(@NonNull ContentResolver cr, @NonNull List<Uri> notifyUris) {
-        Preconditions.checkNotNull(cr);
-        Preconditions.checkNotNull(notifyUris);
+        Objects.requireNonNull(cr);
+        Objects.requireNonNull(notifyUris);
 
         setNotificationUris(cr, notifyUris, cr.getUserId(), true);
     }
diff --git a/core/java/android/database/AbstractWindowedCursor.java b/core/java/android/database/AbstractWindowedCursor.java
index a988f068..daf7d2b 100644
--- a/core/java/android/database/AbstractWindowedCursor.java
+++ b/core/java/android/database/AbstractWindowedCursor.java
@@ -16,7 +16,7 @@
 
 package android.database;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A base class for Cursors that store their data in {@link CursorWindow}s.
diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java
index 77a13cf..8ea450c 100644
--- a/core/java/android/database/BulkCursorNative.java
+++ b/core/java/android/database/BulkCursorNative.java
@@ -16,7 +16,7 @@
 
 package android.database;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index 798b783..69ca581 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -16,7 +16,7 @@
 
 package android.database;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index 6873577..063a2d0 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -17,7 +17,7 @@
 package android.database;
 
 import android.annotation.BytesLong;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.database.sqlite.SQLiteClosable;
 import android.database.sqlite.SQLiteException;
diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java
index c9cafaf..4496f80 100644
--- a/core/java/android/database/CursorWrapper.java
+++ b/core/java/android/database/CursorWrapper.java
@@ -16,7 +16,7 @@
 
 package android.database;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.os.Bundle;
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 992da31..4246b84 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -17,7 +17,7 @@
 package android.database;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.OperationApplicationException;
diff --git a/core/java/android/database/MatrixCursor.java b/core/java/android/database/MatrixCursor.java
index b0d399c..050a49a 100644
--- a/core/java/android/database/MatrixCursor.java
+++ b/core/java/android/database/MatrixCursor.java
@@ -16,7 +16,7 @@
 
 package android.database;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 import java.util.ArrayList;
diff --git a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
index 2af06e1..ba546f3 100644
--- a/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
+++ b/core/java/android/database/sqlite/DatabaseObjectNotClosedException.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * An exception that indicates that garbage-collector is finalizing a database object
diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java
index d6a71da..2fca729 100644
--- a/core/java/android/database/sqlite/SQLiteClosable.java
+++ b/core/java/android/database/sqlite/SQLiteClosable.java
@@ -16,7 +16,8 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.Closeable;
 
 /**
diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java
index e3c4098..4559e91 100644
--- a/core/java/android/database/sqlite/SQLiteCursor.java
+++ b/core/java/android/database/sqlite/SQLiteCursor.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.database.AbstractWindowedCursor;
 import android.database.CursorWindow;
 import android.database.DatabaseUtils;
diff --git a/core/java/android/database/sqlite/SQLiteCustomFunction.java b/core/java/android/database/sqlite/SQLiteCustomFunction.java
index 41b78d3..1ace40d 100644
--- a/core/java/android/database/sqlite/SQLiteCustomFunction.java
+++ b/core/java/android/database/sqlite/SQLiteCustomFunction.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 /**
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index caf3e93..44c78aa 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -20,9 +20,9 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
@@ -60,6 +60,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.WeakHashMap;
 
 /**
@@ -2691,7 +2692,7 @@
              */
             @NonNull
             public Builder setJournalMode(@NonNull  String journalMode) {
-                Preconditions.checkNotNull(journalMode);
+                Objects.requireNonNull(journalMode);
                 mJournalMode = journalMode;
                 return this;
             }
@@ -2703,7 +2704,7 @@
              */
             @NonNull
             public Builder setSynchronousMode(@NonNull String syncMode) {
-                Preconditions.checkNotNull(syncMode);
+                Objects.requireNonNull(syncMode);
                 mSyncMode = syncMode;
                 return this;
             }
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index fcdaf0a..6a52b72 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.ArrayList;
 import java.util.Locale;
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index 3d0ac61..165f863 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -17,14 +17,13 @@
 package android.database.sqlite;
 
 import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Printer;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.util.ArrayList;
 
 /**
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 62cec0e..3341800 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -19,7 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.database.DatabaseErrorHandler;
 import android.database.SQLException;
@@ -27,9 +27,8 @@
 import android.os.FileUtils;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
 import java.io.File;
+import java.util.Objects;
 
 /**
  * A helper class to manage database creation and version management.
@@ -161,7 +160,7 @@
     private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
             int minimumSupportedVersion,
             @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
-        Preconditions.checkNotNull(openParamsBuilder);
+        Objects.requireNonNull(openParamsBuilder);
         if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
 
         mContext = context;
@@ -245,7 +244,7 @@
      * @throws IllegalStateException if the database is already open
      */
     public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
-        Preconditions.checkNotNull(openParams);
+        Objects.requireNonNull(openParams);
         synchronized (this) {
             if (mDatabase != null && mDatabase.isOpen()) {
                 throw new IllegalStateException(
diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java
index 8304133..de1c543 100644
--- a/core/java/android/database/sqlite/SQLiteProgram.java
+++ b/core/java/android/database/sqlite/SQLiteProgram.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.database.DatabaseUtils;
 import android.os.CancellationSignal;
 
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index f7137705..bba14c3 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
diff --git a/core/java/android/database/sqlite/SQLiteSession.java b/core/java/android/database/sqlite/SQLiteSession.java
index a9ac9e7..24b62b8 100644
--- a/core/java/android/database/sqlite/SQLiteSession.java
+++ b/core/java/android/database/sqlite/SQLiteSession.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.database.CursorWindow;
 import android.database.DatabaseUtils;
 import android.os.CancellationSignal;
diff --git a/core/java/android/database/sqlite/SQLiteStatement.java b/core/java/android/database/sqlite/SQLiteStatement.java
index 42e7ac7..9fda8b0 100644
--- a/core/java/android/database/sqlite/SQLiteStatement.java
+++ b/core/java/android/database/sqlite/SQLiteStatement.java
@@ -16,7 +16,7 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.ParcelFileDescriptor;
 
 /**
diff --git a/core/java/android/database/sqlite/SqliteWrapper.java b/core/java/android/database/sqlite/SqliteWrapper.java
index e317164..f335fbd 100644
--- a/core/java/android/database/sqlite/SqliteWrapper.java
+++ b/core/java/android/database/sqlite/SqliteWrapper.java
@@ -17,12 +17,11 @@
 
 package android.database.sqlite;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.util.Log;
 import android.widget.Toast;
diff --git a/core/java/android/ddm/DdmHandleAppName.java b/core/java/android/ddm/DdmHandleAppName.java
index de7acba..4f55921 100644
--- a/core/java/android/ddm/DdmHandleAppName.java
+++ b/core/java/android/ddm/DdmHandleAppName.java
@@ -16,7 +16,7 @@
 
 package android.ddm;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Log;
 
 import org.apache.harmony.dalvik.ddmc.Chunk;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 8e8a81d..25279b3 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -16,14 +16,20 @@
 
 package android.hardware;
 
-import static android.system.OsConstants.*;
+import static android.system.OsConstants.EACCES;
+import static android.system.OsConstants.EBUSY;
+import static android.system.OsConstants.EINVAL;
+import static android.system.OsConstants.ENODEV;
+import static android.system.OsConstants.ENOSYS;
+import static android.system.OsConstants.EOPNOTSUPP;
+import static android.system.OsConstants.EUSERS;
 
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.graphics.Point;
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 5b25f5a..e97a5b9 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -20,7 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.LongDef;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.GraphicBuffer;
 import android.os.Build;
 import android.os.Parcel;
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index e78fb7f..a71a7b6 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -18,7 +18,7 @@
 package android.hardware;
 
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 /**
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index 8c910b2..64c45bf 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -16,7 +16,7 @@
 
 package android.hardware;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * This class represents a {@link android.hardware.Sensor Sensor} event and
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 524a0c4..6bf754f 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -18,7 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
diff --git a/core/java/android/hardware/SerialManager.java b/core/java/android/hardware/SerialManager.java
index 571c3cc..b51382e 100644
--- a/core/java/android/hardware/SerialManager.java
+++ b/core/java/android/hardware/SerialManager.java
@@ -17,7 +17,7 @@
 package android.hardware;
 
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
diff --git a/core/java/android/hardware/SerialPort.java b/core/java/android/hardware/SerialPort.java
index 78ac3c0..0fcaa49 100644
--- a/core/java/android/hardware/SerialPort.java
+++ b/core/java/android/hardware/SerialPort.java
@@ -16,12 +16,11 @@
 
 package android.hardware;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.ParcelFileDescriptor;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
-
 import java.nio.ByteBuffer;
 
 /**
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 7abfabf..974913b 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,7 +16,7 @@
 
 package android.hardware;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index d28b7c5..5a13651 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -18,8 +18,7 @@
 
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 
-import android.annotation.UnsupportedAppUsage;
-
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Interface containing all of the biometric modality agnostic constants.
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index b025508..5c74456 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -16,8 +16,8 @@
 
 package android.hardware.biometrics;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.KeyguardManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.fingerprint.FingerprintManager;
 
 /**
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 7e1506f..4c114fd 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -90,19 +90,19 @@
          * @hide
          */
         @SystemApi
-        int EMPTY_SET = 0x0;
+        int EMPTY_SET = 0x0000;
 
         /**
          * Placeholder for the theoretical strongest biometric security tier.
          * @hide
          */
-        int BIOMETRIC_MAX_STRENGTH = 0x001;
+        int BIOMETRIC_MAX_STRENGTH = 0x0001;
 
         /**
          * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
          * requirements for <strong>Strong</strong>, as defined by the Android CDD.
          */
-        int BIOMETRIC_STRONG = 0x00F;
+        int BIOMETRIC_STRONG = 0x000F;
 
         /**
          * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
@@ -111,7 +111,7 @@
          * <p>Note that this is a superset of {@link #BIOMETRIC_STRONG} and is defined such that
          * <code>BIOMETRIC_STRONG | BIOMETRIC_WEAK == BIOMETRIC_WEAK</code>.
          */
-        int BIOMETRIC_WEAK = 0x0FF;
+        int BIOMETRIC_WEAK = 0x00FF;
 
         /**
          * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
@@ -121,7 +121,7 @@
          * @hide
          */
         @SystemApi
-        int BIOMETRIC_CONVENIENCE = 0xFFF;
+        int BIOMETRIC_CONVENIENCE = 0x0FFF;
 
         /**
          * Placeholder for the theoretical weakest biometric security tier.
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index b605866..a45648f 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
@@ -1212,7 +1212,7 @@
     /**
      * <p>Minimum and maximum zoom ratios supported by this camera device.</p>
      * <p>If the camera device supports zoom-out from 1x zoom, minZoom will be less than 1.0, and
-     * setting android.control.zoomRation to values less than 1.0 increases the camera's field
+     * setting {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} to values less than 1.0 increases the camera's field
      * of view.</p>
      * <p><b>Units</b>: A pair of zoom ratio in floating points: (minZoom, maxZoom)</p>
      * <p><b>Range of valid values:</b><br></p>
@@ -1222,6 +1222,7 @@
      * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
+     * @see CaptureRequest#CONTROL_ZOOM_RATIO
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      */
     @PublicKey
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6bf5783..b9af8f5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.PublicKey;
 import android.hardware.camera2.impl.SyntheticKey;
@@ -2154,7 +2154,7 @@
      * stream combinations of LIMITED hardware level are guaranteed.</p>
      * <p>For a logical multi-camera, bokeh may be implemented by stereo vision from sub-cameras
      * with different field of view. As a result, when bokeh mode is enabled, the camera device
-     * may override android.scaler.CropRegion, and the field of view will be smaller than when
+     * may override {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, and the field of view will be smaller than when
      * bokeh mode is off.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -2163,6 +2163,8 @@
      *   <li>{@link #CONTROL_BOKEH_MODE_CONTINUOUS CONTINUOUS}</li>
      * </ul></p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SCALER_CROP_REGION
      * @see #CONTROL_BOKEH_MODE_OFF
      * @see #CONTROL_BOKEH_MODE_STILL_CAPTURE
      * @see #CONTROL_BOKEH_MODE_CONTINUOUS
@@ -2740,32 +2742,70 @@
      * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
      * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
      * the top-left pixel of the active array.</p>
-     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
-     * system depends on the mode being set.
-     * When the distortion correction mode is OFF, the coordinate system follows
-     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
-     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
-     * When the distortion correction mode is not OFF, the coordinate system follows
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
-     * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
-     * <p>Output streams use this rectangle to produce their output,
-     * cropping to a smaller region if necessary to maintain the
-     * stream's aspect ratio, then scaling the sensor input to
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate system
+     * depends on the mode being set.  When the distortion correction mode is OFF, the
+     * coordinate system follows {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with <code>(0,
+     * 0)</code> being the top-left pixel of the pre-correction active array.  When the distortion
+     * correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the top-left pixel of the
+     * active array.</p>
+     * <p>Output streams use this rectangle to produce their output, cropping to a smaller region
+     * if necessary to maintain the stream's aspect ratio, then scaling the sensor input to
      * match the output's configured resolution.</p>
-     * <p>The crop region is applied after the RAW to other color
-     * space (e.g. YUV) conversion. Since raw streams
-     * (e.g. RAW16) don't have the conversion stage, they are not
+     * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+     * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, they are not
      * croppable. The crop region will be ignored by raw streams.</p>
-     * <p>For non-raw streams, any additional per-stream cropping will
-     * be done to maximize the final pixel area of the stream.</p>
-     * <p>For example, if the crop region is set to a 4:3 aspect
-     * ratio, then 4:3 streams will use the exact crop
-     * region. 16:9 streams will further crop vertically
-     * (letterbox).</p>
-     * <p>Conversely, if the crop region is set to a 16:9, then 4:3
-     * outputs will crop horizontally (pillarbox), and 16:9
-     * streams will match exactly. These additional crops will
-     * be centered within the crop region.</p>
+     * <p>For non-raw streams, any additional per-stream cropping will be done to maximize the
+     * final pixel area of the stream.</p>
+     * <p>For example, if the crop region is set to a 4:3 aspect ratio, then 4:3 streams will use
+     * the exact crop region. 16:9 streams will further crop vertically (letterbox).</p>
+     * <p>Conversely, if the crop region is set to a 16:9, then 4:3 outputs will crop horizontally
+     * (pillarbox), and 16:9 streams will match exactly. These additional crops will be
+     * centered within the crop region.</p>
+     * <p>To illustrate, here are several scenarios of different crop regions and output streams,
+     * for a hypothetical camera device with an active array of size <code>(2000,1500)</code>.  Note that
+     * several of these examples use non-centered crop regions for ease of illustration; such
+     * regions are only supported on devices with FREEFORM capability
+     * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>), but this does not affect the way the crop
+     * rules work otherwise.</p>
+     * <ul>
+     * <li>Camera Configuration:<ul>
+     * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+     * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+     * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
+     * </ul>
+     * </li>
+     * <li>Case #1: 4:3 crop region with 2x digital zoom<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1500, 1125) // (left, top, right, bottom)</code></li>
+     * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(500, 375, 1500, 1125)</code> (equal to crop region)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * <li>Case #2: 16:9 crop region with ~1.5x digital zoom.<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1833, 1125)</code></li>
+     * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(666, 375, 1666, 1125)</code> (pillarboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 375, 1833, 1125)</code> (equal to crop region)</li>
+     * </ul>
+     * </li>
+     * <li>Case #3: 1:1 crop region with ~2.6x digital zoom.<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1250, 1125)</code></li>
+     * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(500, 469, 1250, 1031)</code> (letterboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 543, 1250, 957)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * <li>Case #4: Replace <code>640x480</code> stream with <code>1024x1024</code> stream, with 4:3 crop region:<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1500, 1125)</code></li>
+     * <li><img alt="Square output, 4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png" /></li>
+     * <li><code>1024x1024</code> stream source area: <code>(625, 375, 1375, 1125)</code> (pillarboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+     * <li>Note that in this case, neither of the two outputs is a subset of the other, with
+     *   each containing image data the other doesn't have.</li>
+     * </ul>
+     * </li>
+     * </ul>
      * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, the width and height
      * of the crop region cannot be set to be smaller than
      * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
@@ -2776,18 +2816,16 @@
      * and
      * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
      * respectively.</p>
-     * <p>The camera device may adjust the crop region to account
-     * for rounding and other hardware requirements; the final
-     * crop region used will be included in the output capture
-     * result.</p>
+     * <p>The camera device may adjust the crop region to account for rounding and other hardware
+     * requirements; the final crop region used will be included in the output capture result.</p>
      * <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
      * to take advantage of better support for zoom with logical multi-camera. The benefits
      * include better precision with optical-digital zoom combination, and ability to do
      * zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
      * the capture request must be either letterboxing or pillarboxing (but not both). The
      * coordinate system is post-zoom, meaning that the activeArraySize or
-     * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.
-     * See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+     * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.  See
+     * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
      * <p><b>Units</b>: Pixel coordinates relative to
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -2797,6 +2835,7 @@
      * @see CaptureRequest#CONTROL_ZOOM_RATIO
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index c995623..9b305b32 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
 import android.hardware.camera2.impl.PublicKey;
@@ -2384,7 +2384,7 @@
      * stream combinations of LIMITED hardware level are guaranteed.</p>
      * <p>For a logical multi-camera, bokeh may be implemented by stereo vision from sub-cameras
      * with different field of view. As a result, when bokeh mode is enabled, the camera device
-     * may override android.scaler.CropRegion, and the field of view will be smaller than when
+     * may override {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}, and the field of view will be smaller than when
      * bokeh mode is off.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -2393,6 +2393,8 @@
      *   <li>{@link #CONTROL_BOKEH_MODE_CONTINUOUS CONTINUOUS}</li>
      * </ul></p>
      * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#SCALER_CROP_REGION
      * @see #CONTROL_BOKEH_MODE_OFF
      * @see #CONTROL_BOKEH_MODE_STILL_CAPTURE
      * @see #CONTROL_BOKEH_MODE_CONTINUOUS
@@ -3379,32 +3381,70 @@
      * <p>For devices not supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
      * system always follows that of {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being
      * the top-left pixel of the active array.</p>
-     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate
-     * system depends on the mode being set.
-     * When the distortion correction mode is OFF, the coordinate system follows
-     * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with
-     * <code>(0, 0)</code> being the top-left pixel of the pre-correction active array.
-     * When the distortion correction mode is not OFF, the coordinate system follows
-     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with
-     * <code>(0, 0)</code> being the top-left pixel of the active array.</p>
-     * <p>Output streams use this rectangle to produce their output,
-     * cropping to a smaller region if necessary to maintain the
-     * stream's aspect ratio, then scaling the sensor input to
+     * <p>For devices supporting {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} control, the coordinate system
+     * depends on the mode being set.  When the distortion correction mode is OFF, the
+     * coordinate system follows {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize}, with <code>(0,
+     * 0)</code> being the top-left pixel of the pre-correction active array.  When the distortion
+     * correction mode is not OFF, the coordinate system follows
+     * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, with <code>(0, 0)</code> being the top-left pixel of the
+     * active array.</p>
+     * <p>Output streams use this rectangle to produce their output, cropping to a smaller region
+     * if necessary to maintain the stream's aspect ratio, then scaling the sensor input to
      * match the output's configured resolution.</p>
-     * <p>The crop region is applied after the RAW to other color
-     * space (e.g. YUV) conversion. Since raw streams
-     * (e.g. RAW16) don't have the conversion stage, they are not
+     * <p>The crop region is applied after the RAW to other color space (e.g. YUV)
+     * conversion. Since raw streams (e.g. RAW16) don't have the conversion stage, they are not
      * croppable. The crop region will be ignored by raw streams.</p>
-     * <p>For non-raw streams, any additional per-stream cropping will
-     * be done to maximize the final pixel area of the stream.</p>
-     * <p>For example, if the crop region is set to a 4:3 aspect
-     * ratio, then 4:3 streams will use the exact crop
-     * region. 16:9 streams will further crop vertically
-     * (letterbox).</p>
-     * <p>Conversely, if the crop region is set to a 16:9, then 4:3
-     * outputs will crop horizontally (pillarbox), and 16:9
-     * streams will match exactly. These additional crops will
-     * be centered within the crop region.</p>
+     * <p>For non-raw streams, any additional per-stream cropping will be done to maximize the
+     * final pixel area of the stream.</p>
+     * <p>For example, if the crop region is set to a 4:3 aspect ratio, then 4:3 streams will use
+     * the exact crop region. 16:9 streams will further crop vertically (letterbox).</p>
+     * <p>Conversely, if the crop region is set to a 16:9, then 4:3 outputs will crop horizontally
+     * (pillarbox), and 16:9 streams will match exactly. These additional crops will be
+     * centered within the crop region.</p>
+     * <p>To illustrate, here are several scenarios of different crop regions and output streams,
+     * for a hypothetical camera device with an active array of size <code>(2000,1500)</code>.  Note that
+     * several of these examples use non-centered crop regions for ease of illustration; such
+     * regions are only supported on devices with FREEFORM capability
+     * ({@link CameraCharacteristics#SCALER_CROPPING_TYPE android.scaler.croppingType} <code>== FREEFORM</code>), but this does not affect the way the crop
+     * rules work otherwise.</p>
+     * <ul>
+     * <li>Camera Configuration:<ul>
+     * <li>Active array size: <code>2000x1500</code> (3 MP, 4:3 aspect ratio)</li>
+     * <li>Output stream #1: <code>640x480</code> (VGA, 4:3 aspect ratio)</li>
+     * <li>Output stream #2: <code>1280x720</code> (720p, 16:9 aspect ratio)</li>
+     * </ul>
+     * </li>
+     * <li>Case #1: 4:3 crop region with 2x digital zoom<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1500, 1125) // (left, top, right, bottom)</code></li>
+     * <li><img alt="4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(500, 375, 1500, 1125)</code> (equal to crop region)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * <li>Case #2: 16:9 crop region with ~1.5x digital zoom.<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1833, 1125)</code></li>
+     * <li><img alt="16:9 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(666, 375, 1666, 1125)</code> (pillarboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 375, 1833, 1125)</code> (equal to crop region)</li>
+     * </ul>
+     * </li>
+     * <li>Case #3: 1:1 crop region with ~2.6x digital zoom.<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1250, 1125)</code></li>
+     * <li><img alt="1:1 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png" /></li>
+     * <li><code>640x480</code> stream source area: <code>(500, 469, 1250, 1031)</code> (letterboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 543, 1250, 957)</code> (letterboxed)</li>
+     * </ul>
+     * </li>
+     * <li>Case #4: Replace <code>640x480</code> stream with <code>1024x1024</code> stream, with 4:3 crop region:<ul>
+     * <li>Crop region: <code>Rect(500, 375, 1500, 1125)</code></li>
+     * <li><img alt="Square output, 4:3 aspect ratio crop diagram" src="/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png" /></li>
+     * <li><code>1024x1024</code> stream source area: <code>(625, 375, 1375, 1125)</code> (pillarboxed)</li>
+     * <li><code>1280x720</code> stream source area: <code>(500, 469, 1500, 1031)</code> (letterboxed)</li>
+     * <li>Note that in this case, neither of the two outputs is a subset of the other, with
+     *   each containing image data the other doesn't have.</li>
+     * </ul>
+     * </li>
+     * </ul>
      * <p>If the coordinate system is {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}, the width and height
      * of the crop region cannot be set to be smaller than
      * <code>floor( activeArraySize.width / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code> and
@@ -3415,18 +3455,16 @@
      * and
      * <code>floor( preCorrectionActiveArraySize.height / {@link CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM android.scaler.availableMaxDigitalZoom} )</code>,
      * respectively.</p>
-     * <p>The camera device may adjust the crop region to account
-     * for rounding and other hardware requirements; the final
-     * crop region used will be included in the output capture
-     * result.</p>
+     * <p>The camera device may adjust the crop region to account for rounding and other hardware
+     * requirements; the final crop region used will be included in the output capture result.</p>
      * <p>Starting from API level 30, it's strongly recommended to use {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio}
      * to take advantage of better support for zoom with logical multi-camera. The benefits
      * include better precision with optical-digital zoom combination, and ability to do
      * zoom-out from 1.0x. When using {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for zoom, the crop region in
      * the capture request must be either letterboxing or pillarboxing (but not both). The
      * coordinate system is post-zoom, meaning that the activeArraySize or
-     * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.
-     * See {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
+     * preCorrectionActiveArraySize covers the camera device's field of view "after" zoom.  See
+     * {@link CaptureRequest#CONTROL_ZOOM_RATIO android.control.zoomRatio} for details.</p>
      * <p><b>Units</b>: Pixel coordinates relative to
      * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize} or
      * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} depending on distortion correction
@@ -3436,6 +3474,7 @@
      * @see CaptureRequest#CONTROL_ZOOM_RATIO
      * @see CaptureRequest#DISTORTION_CORRECTION_MODE
      * @see CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM
+     * @see CameraCharacteristics#SCALER_CROPPING_TYPE
      * @see CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE
      * @see CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE
      */
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 41435c9..67e7a4c 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -50,8 +50,6 @@
 import android.util.SparseArray;
 import android.view.Surface;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -60,6 +58,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -2482,7 +2481,7 @@
         private final Handler mHandler;
 
         public CameraHandlerExecutor(@NonNull Handler handler) {
-            mHandler = Preconditions.checkNotNull(handler);
+            mHandler = Objects.requireNonNull(handler);
         }
 
         @Override
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index c3ebe43..1fab666 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -16,13 +16,12 @@
 
 package android.hardware.camera2.impl;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.ImageFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.marshal.MarshalQueryable;
@@ -54,7 +53,6 @@
 import android.hardware.camera2.params.HighSpeedVideoConfiguration;
 import android.hardware.camera2.params.LensShadingMap;
 import android.hardware.camera2.params.MandatoryStreamCombination;
-import android.hardware.camera2.params.MandatoryStreamCombination.MandatoryStreamInformation;
 import android.hardware.camera2.params.OisSample;
 import android.hardware.camera2.params.RecommendedStreamConfiguration;
 import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
@@ -73,14 +71,13 @@
 import android.util.Range;
 import android.util.Size;
 
-import com.android.internal.util.Preconditions;
-
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Implementation of camera metadata marshal/unmarshal across Binder to
@@ -240,6 +237,11 @@
          *
          * <p>This value is looked up the first time, and cached subsequently.</p>
          *
+         * <p>This function may be called without cacheTag() if this is not a vendor key.
+         * If this is a vendor key, cacheTag() must be called first before getTag() can
+         * be called. Otherwise, mVendorId could be default (Long.MAX_VALUE) and vendor
+         * tag lookup could fail.</p>
+         *
          * @return The tag numeric value corresponding to the string
          */
         @UnsupportedAppUsage
@@ -252,6 +254,27 @@
         }
 
         /**
+         * Whether this key's tag is cached.
+         *
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public final boolean hasTag() {
+            return mHasTag;
+        }
+
+        /**
+         * Cache this key's tag.
+         *
+         * @hide
+         */
+        @UnsupportedAppUsage
+        public final void cacheTag(int tag) {
+            mHasTag = true;
+            mTag = tag;
+        }
+
+        /**
          * Get the raw class backing the type {@code T} for this key.
          *
          * <p>The distinction is only important if {@code T} is a generic, e.g.
@@ -406,7 +429,7 @@
      * @return the field corresponding to the {@code key}, or {@code null} if no value was set
      */
     public <T> T get(Key<T> key) {
-        Preconditions.checkNotNull(key, "key must not be null");
+        Objects.requireNonNull(key, "key must not be null");
 
         // Check if key has been overridden to use a wrapper class on the java side.
         GetCommand g = sGetCommandMap.get(key);
@@ -523,7 +546,13 @@
     }
 
     private <T> T getBase(Key<T> key) {
-        int tag = nativeGetTagFromKeyLocal(key.getName());
+        int tag;
+        if (key.hasTag()) {
+            tag = key.getTag();
+        } else {
+            tag = nativeGetTagFromKeyLocal(key.getName());
+            key.cacheTag(tag);
+        }
         byte[] values = readValues(tag);
         if (values == null) {
             // If the key returns null, use the fallback key if exists.
@@ -1451,7 +1480,13 @@
     }
 
     private <T> void setBase(Key<T> key, T value) {
-        int tag = nativeGetTagFromKeyLocal(key.getName());
+        int tag;
+        if (key.hasTag()) {
+            tag = key.getTag();
+        } else {
+            tag = nativeGetTagFromKeyLocal(key.getName());
+            key.cacheTag(tag);
+        }
         if (value == null) {
             // Erase the entry
             writeValues(tag, /*src*/null);
diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
index 283977f..c3dc25f 100644
--- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java
+++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java
@@ -16,9 +16,8 @@
 
 package android.hardware.camera2.params;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Immutable class to store a 4-element vector of integers corresponding to a 2x2 pattern
@@ -88,7 +87,7 @@
      * @throws NullPointerException if the destination is null.
      */
     public void copyTo(int[] destination, int offset) {
-        checkNotNull(destination, "destination must not be null");
+        Objects.requireNonNull(destination, "destination must not be null");
         if (offset < 0) {
             throw new IllegalArgumentException("Null offset passed to copyTo");
         }
diff --git a/core/java/android/hardware/camera2/params/LensShadingMap.java b/core/java/android/hardware/camera2/params/LensShadingMap.java
index 9eb276f..38c1dd4 100644
--- a/core/java/android/hardware/camera2/params/LensShadingMap.java
+++ b/core/java/android/hardware/camera2/params/LensShadingMap.java
@@ -25,12 +25,12 @@
 import static com.android.internal.util.Preconditions.checkArgumentNonnegative;
 import static com.android.internal.util.Preconditions.checkArgumentPositive;
 import static com.android.internal.util.Preconditions.checkArrayElementsInRange;
-import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.utils.HashCodeHelpers;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
@@ -70,7 +70,7 @@
 
         mRows = checkArgumentPositive(rows, "rows must be positive");
         mColumns = checkArgumentPositive(columns, "columns must be positive");
-        mElements = checkNotNull(elements, "elements must not be null");
+        mElements = Objects.requireNonNull(elements, "elements must not be null");
 
         if (elements.length != getGainFactorCount()) {
             throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
@@ -203,7 +203,7 @@
      */
     public void copyGainFactors(final float[] destination, final int offset) {
         checkArgumentNonnegative(offset, "offset must not be negative");
-        checkNotNull(destination, "destination must not be null");
+        Objects.requireNonNull(destination, "destination must not be null");
         if (destination.length + offset < getGainFactorCount()) {
             throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
         }
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 336edc1..478922d 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -17,7 +17,6 @@
 package android.hardware.camera2.params;
 
 import static com.android.internal.util.Preconditions.checkArrayElementsNotNull;
-import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.graphics.ImageFormat;
 import android.graphics.PixelFormat;
@@ -491,7 +490,7 @@
      * @see #isOutputSupportedFor(Surface)
      */
     public static <T> boolean isOutputSupportedFor(Class<T> klass) {
-        checkNotNull(klass, "klass must not be null");
+        Objects.requireNonNull(klass, "klass must not be null");
 
         if (klass == android.media.ImageReader.class) {
             return true;
@@ -548,7 +547,7 @@
      * @see #isOutputSupportedFor(Class)
      */
     public boolean isOutputSupportedFor(Surface surface) {
-        checkNotNull(surface, "surface must not be null");
+        Objects.requireNonNull(surface, "surface must not be null");
 
         Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
         int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
@@ -897,7 +896,7 @@
      * @see PixelFormat
      */
     public long getOutputMinFrameDuration(int format, Size size) {
-        checkNotNull(size, "size must not be null");
+        Objects.requireNonNull(size, "size must not be null");
         checkArgumentFormatSupported(format, /*output*/true);
 
         return getInternalFormatDuration(imageFormatToInternal(format),
diff --git a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
index 526f086..16f3f2a 100644
--- a/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
+++ b/core/java/android/hardware/camera2/utils/HashCodeHelpers.java
@@ -16,7 +16,7 @@
 
 package android.hardware.camera2.utils;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Provide hashing functions using the Modified Bernstein hash
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index d3c4505..abe1372 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -16,7 +16,7 @@
 
 package android.hardware.camera2.utils;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.legacy.LegacyCameraDevice;
 import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java
index d9ba31b..435ed15 100644
--- a/core/java/android/hardware/camera2/utils/TypeReference.java
+++ b/core/java/android/hardware/camera2/utils/TypeReference.java
@@ -16,7 +16,10 @@
 
 package android.hardware.camera2.utils;
 
-import android.annotation.UnsupportedAppUsage;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.lang.reflect.Array;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.ParameterizedType;
@@ -24,8 +27,6 @@
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
 
-import static com.android.internal.util.Preconditions.*;
-
 /**
  * Super type token; allows capturing generic types at runtime by forcing them to be reified.
  *
diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
index 350bc30..26fd265 100644
--- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java
+++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java
@@ -27,6 +27,7 @@
 
 import java.time.LocalDate;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * AmbientBrightnessDayStats stores and manipulates brightness stats over a single day.
@@ -70,8 +71,8 @@
      */
     public AmbientBrightnessDayStats(@NonNull LocalDate localDate,
             @NonNull float[] bucketBoundaries, float[] stats) {
-        Preconditions.checkNotNull(localDate);
-        Preconditions.checkNotNull(bucketBoundaries);
+        Objects.requireNonNull(localDate);
+        Objects.requireNonNull(bucketBoundaries);
         Preconditions.checkArrayElementsInRange(bucketBoundaries, 0, Float.MAX_VALUE,
                 "bucketBoundaries");
         if (bucketBoundaries.length < 1) {
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 0a38538..13122d2 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -541,8 +541,8 @@
          * @throws IllegalArgumentException if the nit levels are not monotonically increasing.
          */
         public Builder(float[] lux, float[] nits) {
-            Preconditions.checkNotNull(lux);
-            Preconditions.checkNotNull(nits);
+            Objects.requireNonNull(lux);
+            Objects.requireNonNull(nits);
             if (lux.length == 0 || nits.length == 0) {
                 throw new IllegalArgumentException("Lux and nits arrays must not be empty");
             }
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0b25dbd..799dff9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -23,8 +23,8 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.KeyguardManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Point;
 import android.media.projection.MediaProjection;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index ea63776..2a58495 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java
index 55e6051..5bbbbf9 100644
--- a/core/java/android/hardware/display/WifiDisplay.java
+++ b/core/java/android/hardware/display/WifiDisplay.java
@@ -16,7 +16,7 @@
 
 package android.hardware.display;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/hardware/display/WifiDisplayStatus.java b/core/java/android/hardware/display/WifiDisplayStatus.java
index 1973e51..e2a825f 100644
--- a/core/java/android/hardware/display/WifiDisplayStatus.java
+++ b/core/java/android/hardware/display/WifiDisplayStatus.java
@@ -16,7 +16,7 @@
 
 package android.hardware.display;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 22f8590..7bc4529 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -26,8 +26,8 @@
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 0c0f248..8d32db0 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -20,7 +20,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.os.Binder;
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index 23d8d01..a1866af 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -16,7 +16,7 @@
 package android.hardware.location;
 
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.location.Location;
 import android.os.Build;
 import android.os.RemoteException;
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 5484df4..55505ba 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -27,8 +27,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.AudioFormat;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
@@ -44,12 +44,10 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.UUID;
 
 /**
- * The SoundTrigger class provides access via JNI to the native service managing
- * the sound trigger HAL.
+ * The SoundTrigger class provides access to the service managing the sound trigger HAL.
  *
  * @hide
  */
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 7cf5600..7291419 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -18,11 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.media.soundtrigger_middleware.ISoundTriggerCallback;
 import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
 import android.media.soundtrigger_middleware.ISoundTriggerModule;
-import android.media.soundtrigger_middleware.ModelParameterRange;
 import android.media.soundtrigger_middleware.PhraseRecognitionEvent;
 import android.media.soundtrigger_middleware.PhraseSoundModel;
 import android.media.soundtrigger_middleware.RecognitionEvent;
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index a725205..a94266b 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -26,6 +26,8 @@
 import com.android.internal.util.Preconditions;
 import java.util.Objects;
 
+import java.util.Objects;
+
 /**
  * A class representing a USB accessory, which is an external hardware component
  * that communicates with an android application over USB.
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 67e05c8..a0f64cd 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -18,13 +18,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
 
 import com.android.internal.util.Preconditions;
+
 import java.util.Objects;
 
 /**
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 1a5cdd9..53a5785 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index eb148b9..73b9d17 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -26,8 +26,8 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index d464ab5..473df71 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -17,7 +17,7 @@
 package android.hardware.usb;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.util.Log;
 
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index 7d4849f..9327b24 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -16,7 +16,7 @@
 
 package android.inputmethodservice;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 62f0196..4d5fabb 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -18,7 +18,7 @@
 
 import android.annotation.BinderThread;
 import android.annotation.MainThread;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index a45f703..8e52ee9 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -34,9 +34,9 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -517,7 +517,9 @@
         @Override
         public void onCreateInlineSuggestionsRequest(ComponentName componentName,
                 AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
-            Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
+            if (DEBUG) {
+                Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
+            }
             handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
         }
 
@@ -722,15 +724,6 @@
     }
 
     /**
-     * Returns whether inline suggestions are enabled on this service.
-     *
-     * TODO(b/137800469): check XML for value.
-     */
-    private boolean isInlineSuggestionsEnabled() {
-        return true;
-    }
-
-    /**
      * Sends an {@link InlineSuggestionsRequest} obtained from
      * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
      * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
@@ -763,22 +756,18 @@
 
     private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
             @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
-        mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
-                callback);
-
-        if (!isInlineSuggestionsEnabled()) {
+        if (!mInputStarted) {
             try {
+                Log.w(TAG, "onStartInput() not called yet");
                 callback.onInlineSuggestionsUnsupported();
             } catch (RemoteException e) {
-                Log.w(TAG, "handleMakeInlineSuggestionsRequest() RemoteException:" + e);
+                Log.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
             }
             return;
         }
 
-        if (!mInputStarted) {
-            Log.w(TAG, "onStartInput() not called yet");
-            return;
-        }
+        mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
+                callback);
 
         makeInlineSuggestionsRequest();
     }
@@ -786,8 +775,12 @@
     private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
             @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
         if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
-            Log.d(TAG, "Response component=" + componentName + " differs from request component="
-                    + mInlineSuggestionsRequestInfo.mComponentName + ", ignoring response");
+            if (DEBUG) {
+                Log.d(TAG,
+                        "Response component=" + componentName + " differs from request component="
+                                + mInlineSuggestionsRequestInfo.mComponentName
+                                + ", ignoring response");
+            }
             return;
         }
         onInlineSuggestionsResponse(response);
@@ -859,7 +852,7 @@
          */
         public boolean validate(ComponentName componentName) {
             final boolean result = componentName.equals(mComponentName);
-            if (!result) {
+            if (DEBUG && !result) {
                 Log.d(TAG, "Cached request info ComponentName=" + mComponentName
                         + " differs from received ComponentName=" + componentName);
             }
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index 3f25113..c85fcd9 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -16,8 +16,8 @@
 
 package android.inputmethodservice;
 
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.XmlRes;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 45f067b..b780b21 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -16,7 +16,7 @@
 
 package android.inputmethodservice;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3ed51d7..6da16a8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -27,8 +27,8 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.net.IpSecManager.UdpEncapsulationSocket;
@@ -3072,6 +3072,7 @@
      * @hide
      */
     @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_AIRPLANE_MODE,
             android.Manifest.permission.NETWORK_SETTINGS,
             android.Manifest.permission.NETWORK_SETUP_WIZARD,
             android.Manifest.permission.NETWORK_STACK})
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
index 97be51a..059cd94 100644
--- a/core/java/android/net/DhcpResults.java
+++ b/core/java/android/net/DhcpResults.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.shared.InetAddressUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index 7256502..fd015b4 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -17,7 +17,7 @@
 package android.net;
 
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Message;
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 1ae44e1..37425ff 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java
index dddb64d..23d5ff7 100644
--- a/core/java/android/net/IpConfiguration.java
+++ b/core/java/android/net/IpConfiguration.java
@@ -20,8 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
-import android.net.StaticIpConfiguration;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 93dd2e4..bf8b38f 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -30,7 +30,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index ed509cb..2792c56 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -63,6 +63,7 @@
     private String mPrivateDnsServerName;
     private String mDomains;
     private ArrayList<RouteInfo> mRoutes = new ArrayList<>();
+    private Inet4Address mDhcpServerAddress;
     private ProxyInfo mHttpProxy;
     private int mMtu;
     // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max"
@@ -196,6 +197,7 @@
                 addStackedLink(l);
             }
             setMtu(source.mMtu);
+            setDhcpServerAddress(source.getDhcpServerAddress());
             mTcpBufferSizes = source.mTcpBufferSizes;
             mNat64Prefix = source.mNat64Prefix;
             mWakeOnLanSupported = source.mWakeOnLanSupported;
@@ -460,6 +462,24 @@
     }
 
     /**
+     * Set DHCP server address.
+     *
+     * @param serverAddress the server address to set.
+     */
+    public void setDhcpServerAddress(@Nullable Inet4Address serverAddress) {
+        mDhcpServerAddress = serverAddress;
+    }
+
+     /**
+     * Get DHCP server address
+     *
+     * @return The current DHCP server address.
+     */
+    public @Nullable Inet4Address getDhcpServerAddress() {
+        return mDhcpServerAddress;
+    }
+
+    /**
      * Returns the private DNS server name that is in use. If not {@code null},
      * private DNS is in strict mode. In this mode, applications should ensure
      * that all DNS queries are encrypted and sent to this hostname and that
@@ -851,6 +871,7 @@
         mHttpProxy = null;
         mStackedLinks.clear();
         mMtu = 0;
+        mDhcpServerAddress = null;
         mTcpBufferSizes = null;
         mNat64Prefix = null;
         mWakeOnLanSupported = false;
@@ -919,6 +940,11 @@
             resultJoiner.add("WakeOnLanSupported: true");
         }
 
+        if (mDhcpServerAddress != null) {
+            resultJoiner.add("ServerAddress:");
+            resultJoiner.add(mDhcpServerAddress.toString());
+        }
+
         if (mTcpBufferSizes != null) {
             resultJoiner.add("TcpBufferSizes:");
             resultJoiner.add(mTcpBufferSizes);
@@ -1273,6 +1299,17 @@
     }
 
     /**
+     * Compares this {@code LinkProperties} DHCP server address against the target
+     *
+     * @param target LinkProperties to compare.
+     * @return {@code true} if both are identical, {@code false} otherwise.
+     * @hide
+     */
+    public boolean isIdenticalDhcpServerAddress(@NonNull LinkProperties target) {
+        return Objects.equals(mDhcpServerAddress, target.mDhcpServerAddress);
+    }
+
+    /**
      * Compares this {@code LinkProperties} interface addresses against the target
      *
      * @param target LinkProperties to compare.
@@ -1489,6 +1526,7 @@
          */
         return isIdenticalInterfaceName(target)
                 && isIdenticalAddresses(target)
+                && isIdenticalDhcpServerAddress(target)
                 && isIdenticalDnses(target)
                 && isIdenticalPrivateDns(target)
                 && isIdenticalValidatedPrivateDnses(target)
@@ -1613,6 +1651,7 @@
                 + mMtu * 51
                 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
                 + (mUsePrivateDns ? 57 : 0)
+                + ((null == mDhcpServerAddress) ? 0 : mDhcpServerAddress.hashCode())
                 + mPcscfs.size() * 67
                 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
                 + Objects.hash(mNat64Prefix)
@@ -1635,6 +1674,7 @@
         dest.writeString(mPrivateDnsServerName);
         writeAddresses(dest, mPcscfs);
         dest.writeString(mDomains);
+        writeAddress(dest, mDhcpServerAddress);
         dest.writeInt(mMtu);
         dest.writeString(mTcpBufferSizes);
         dest.writeInt(mRoutes.size());
@@ -1663,8 +1703,9 @@
         }
     }
 
-    private static void writeAddress(@NonNull Parcel dest, @NonNull InetAddress addr) {
-        dest.writeByteArray(addr.getAddress());
+    private static void writeAddress(@NonNull Parcel dest, @Nullable InetAddress addr) {
+        byte[] addressBytes = (addr == null ? null : addr.getAddress());
+        dest.writeByteArray(addressBytes);
         if (addr instanceof Inet6Address) {
             final Inet6Address v6Addr = (Inet6Address) addr;
             final boolean hasScopeId = v6Addr.getScopeId() != 0;
@@ -1673,9 +1714,11 @@
         }
     }
 
-    @NonNull
+    @Nullable
     private static InetAddress readAddress(@NonNull Parcel p) throws UnknownHostException {
         final byte[] addr = p.createByteArray();
+        if (addr == null) return null;
+
         if (addr.length == INET6_ADDR_LENGTH) {
             final boolean hasScopeId = p.readBoolean();
             final int scopeId = hasScopeId ? p.readInt() : 0;
@@ -1722,6 +1765,10 @@
                     } catch (UnknownHostException e) { }
                 }
                 netProp.setDomains(in.readString());
+                try {
+                    netProp.setDhcpServerAddress((Inet4Address) InetAddress
+                            .getByAddress(in.createByteArray()));
+                } catch (UnknownHostException e) { }
                 netProp.setMtu(in.readInt());
                 netProp.setTcpBufferSizes(in.readString());
                 addressCount = in.readInt();
diff --git a/core/java/android/net/LinkQualityInfo.java b/core/java/android/net/LinkQualityInfo.java
index 2e6915f..aa56cff 100644
--- a/core/java/android/net/LinkQualityInfo.java
+++ b/core/java/android/net/LinkQualityInfo.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 6a2031b..5b38f78 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -16,7 +16,8 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.IOException;
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index b066a15..e80e3a6 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.system.ErrnoException;
 import android.system.Int32Ref;
 import android.system.Os;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 8729514..74c9aac 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/net/MobileLinkQualityInfo.java b/core/java/android/net/MobileLinkQualityInfo.java
index a10a14d..a65de6b 100644
--- a/core/java/android/net/MobileLinkQualityInfo.java
+++ b/core/java/android/net/MobileLinkQualityInfo.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 
 /**
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index c6c73fe..c5681cb 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.system.ErrnoException;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index ff4bf2d..60bd573 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,7 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.Bundle;
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 4cee5f3..33c39d4 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.os.Build;
 import android.os.Parcel;
diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java
index 5b1d12c..42ab925 100644
--- a/core/java/android/net/NetworkFactory.java
+++ b/core/java/android/net/NetworkFactory.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.os.Handler;
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 92f105f..d0c5363 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -17,7 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 33baebb..4f05c9b 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.BackupUtils;
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 9150aae..de962f8 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -19,8 +19,8 @@
 import static android.content.pm.PackageManager.GET_SIGNATURES;
 
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
diff --git a/core/java/android/net/NetworkQuotaInfo.java b/core/java/android/net/NetworkQuotaInfo.java
index a46cdde..2e52d9c 100644
--- a/core/java/android/net/NetworkQuotaInfo.java
+++ b/core/java/android/net/NetworkQuotaInfo.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 2992127..9731f3c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.NetworkCapabilities.NetCapability;
 import android.net.NetworkCapabilities.Transport;
 import android.os.Build;
@@ -247,9 +247,8 @@
          * removing even the capabilities that are set by default when the object is constructed.
          *
          * @return The builder to facilitate chaining.
-         * @hide
          */
-        @UnsupportedAppUsage
+        @NonNull
         public Builder clearCapabilities() {
             mNetworkCapabilities.clearAll();
             return this;
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index 2bc3eb5..cf31d21 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -16,6 +16,9 @@
 
 package android.net;
 
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
 /**
  * Describes specific properties of a requested network for use in a {@link NetworkRequest}.
  *
@@ -31,7 +34,8 @@
      *
      * @hide
      */
-    public abstract boolean satisfiedBy(NetworkSpecifier other);
+    @SystemApi
+    public abstract boolean satisfiedBy(@Nullable NetworkSpecifier other);
 
     /**
      * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
@@ -45,6 +49,7 @@
      *
      * @hide
      */
+    @SystemApi
     public void assertValidFromUid(int requestorUid) {
         // empty
     }
@@ -68,6 +73,8 @@
      *
      * @hide
      */
+    @SystemApi
+    @Nullable
     public NetworkSpecifier redact() {
         // TODO (b/122160111): convert default to null once all platform NetworkSpecifiers
         // implement this method.
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index 292cf50..e449615 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 6c7aa13..45bf4d2 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,7 +19,7 @@
 import static android.os.Process.CLAT_UID;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index d96d2ee..6d46c20 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -30,7 +30,7 @@
 
 import static com.android.internal.util.ArrayUtils.total;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.NetworkStatsHistoryBucketProto;
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 87c7118..5e6c47a 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -34,7 +34,7 @@
 import static android.net.NetworkStats.ROAMING_YES;
 import static android.net.wifi.WifiInfo.removeDoubleQuotes;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.BackupUtils;
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d0f54b4..08cc4e2 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -20,7 +20,7 @@
 import static android.system.OsConstants.AF_INET6;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.shared.Inet4AddressUtils;
 import android.os.Build;
 import android.system.ErrnoException;
@@ -61,13 +61,6 @@
     public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
 
     /**
-     * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
-     * @param fd the socket's {@link FileDescriptor}.
-     * @param ifIndex the interface index.
-     */
-    public native static void setupRaSocket(FileDescriptor fd, int ifIndex) throws SocketException;
-
-    /**
      * Binds the current process to the network designated by {@code netId}.  All sockets created
      * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
      * {@link Network#getSocketFactory}) will be bound to this network.  Note that if this
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index a1c7fce..767b693 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -8,4 +8,4 @@
 reminv@google.com
 satk@google.com
 
-per-file SSL*, Uri*, Url* = flooey@google.com, narayan@google.com, tobiast@google.com
+per-file SSL*, Uri*, Url* = prb@google.com, dauletz@google.com, narayan@google.com, tobiast@google.com
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 4600942..4ba7394 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -18,7 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.Log;
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 9d92db4..ffe9ae9 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 52d3fc4..ea6002c 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 39cb323..8b6ac42 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.SystemProperties;
 import android.util.Log;
diff --git a/core/java/android/net/SSLSessionCache.java b/core/java/android/net/SSLSessionCache.java
index 9667e82..944bc54 100644
--- a/core/java/android/net/SSLSessionCache.java
+++ b/core/java/android/net/SSLSessionCache.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.Log;
 
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index f9c2def..8c6faf6 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.SystemClock;
 import android.util.Log;
 
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 990c114..f24a9bd 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.shared.InetAddressUtils;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index bf4884a..162d6e2 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -20,10 +20,10 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.DownloadManager;
 import android.app.backup.BackupManager;
 import android.app.usage.NetworkStatsManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.media.MediaPlayer;
 import android.os.Build;
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 44d977a..525bbfd 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Environment;
 import android.os.Parcel;
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index ed7cddc..4b804b0 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -23,11 +23,11 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/net/WebAddress.java b/core/java/android/net/WebAddress.java
index 994c794..aa3777d 100644
--- a/core/java/android/net/WebAddress.java
+++ b/core/java/android/net/WebAddress.java
@@ -20,7 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 import java.util.Locale;
diff --git a/core/java/android/net/http/OWNERS b/core/java/android/net/http/OWNERS
index 6b8c9ed..3092612 100644
--- a/core/java/android/net/http/OWNERS
+++ b/core/java/android/net/http/OWNERS
@@ -1,3 +1,4 @@
-flooey@google.com
 narayan@google.com
 tobiast@google.com
+include platform/libcore:/OWNERS
+include platform/external/conscrypt:/OWNERS
diff --git a/core/java/android/net/http/SslCertificate.java b/core/java/android/net/http/SslCertificate.java
index 01dd08f..250cff2 100644
--- a/core/java/android/net/http/SslCertificate.java
+++ b/core/java/android/net/http/SslCertificate.java
@@ -17,7 +17,7 @@
 package android.net.http;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Bundle;
 import android.text.format.DateFormat;
diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java
index b3f2fb7..d43c616 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -16,8 +16,9 @@
 
 package android.net.http;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
+
 import java.security.cert.X509Certificate;
 
 /**
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index 8243be9..f93907a 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index eac5579..b221cb9 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 5f9f507..8fc1ef8 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 1c4ef38..9f592e8 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1888,10 +1888,13 @@
     /**
      * Note: currently only works when the requested pid has the same UID
      * as the caller.
+     *
+     * @return true if the meminfo was read successfully, false if not (i.e., given pid has gone).
+     *
      * @hide
      */
     @UnsupportedAppUsage
-    public static native void getMemoryInfo(int pid, MemoryInfo memoryInfo);
+    public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);
 
     /**
      * Retrieves the PSS memory used by the process as given by the
@@ -1904,6 +1907,8 @@
      * array of up to 3 entries to also receive (up to 3 values in order): the Uss and SwapPss and
      * Rss (only filled in as of {@link android.os.Build.VERSION_CODES#P}) of the process, and
      * another array to also retrieve the separate memtrack size.
+     *
+     * @return The PSS memory usage, or 0 if failed to retrieve (i.e., given pid has gone).
      * @hide
      */
     public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
@@ -2590,4 +2595,12 @@
      * @hide
      */
     public static native long getIonMappedSizeKb();
+
+    /**
+     * Return whether virtually-mapped kernel stacks are enabled (CONFIG_VMAP_STACK).
+     * Note: caller needs config_gz read sepolicy permission
+     *
+     * @hide
+     */
+    public static native boolean isVmapStack();
 }
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 37ca868..e62244f 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -84,6 +84,10 @@
         return mAttrs;
     }
 
+    public VibrationAttributes getVibrationAttributes() {
+        return new VibrationAttributes.Builder(mAttrs, null).build();
+    }
+
     /**
      * Mutes the external vibration if it's playing and unmuted.
      *
@@ -157,7 +161,6 @@
         out.writeInt(mUid);
         out.writeString(mPkg);
         writeAudioAttributes(mAttrs, out, flags);
-        out.writeParcelable(mAttrs, flags);
         out.writeStrongBinder(mController.asBinder());
         out.writeStrongBinder(mToken);
     }
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index c5ceecd..2561e1e 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -17,6 +17,7 @@
 
 package android.os;
 
+import android.content.IntentSender;
 import android.os.IRecoverySystemProgressListener;
 
 /** @hide */
@@ -26,4 +27,7 @@
     boolean setupBcb(in String command);
     boolean clearBcb();
     void rebootRecoveryWithCommand(in String command);
+    boolean requestLskf(in String updateToken, in IntentSender sender);
+    boolean clearLskf();
+    boolean rebootWithLskf(in String updateToken, in String reason);
 }
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index e81a505..edaaf81 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -62,7 +62,7 @@
     boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
     UserInfo getProfileParent(int userId);
     boolean isSameProfileGroup(int userId, int otherUserHandle);
-    String getUserTypeForUser(int userId);
+    boolean isUserOfType(int userId, in String userType);
     @UnsupportedAppUsage
     UserInfo getUserInfo(int userId);
     String getUserAccount(int userId);
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 6b881fe..7b2d148 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -16,17 +16,18 @@
 
 package android.os;
 
-import android.media.AudioAttributes;
 import android.os.VibrationEffect;
+import android.os.VibrationAttributes;
 
 /** {@hide} */
 interface IVibratorService
 {
     boolean hasVibrator();
     boolean hasAmplitudeControl();
-    boolean setAlwaysOnEffect(int id, in VibrationEffect effect, in AudioAttributes attributes);
-    void vibrate(int uid, String opPkg, in VibrationEffect effect, in AudioAttributes attributes,
-            String reason, IBinder token);
+    boolean setAlwaysOnEffect(int id, in VibrationEffect effect,
+            in VibrationAttributes attributes);
+    void vibrate(int uid, String opPkg, in VibrationEffect effect,
+            in VibrationAttributes attributes, String reason, IBinder token);
     void cancelVibrate(IBinder token);
 }
 
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 1901820..cdcb3ff 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -18,6 +18,8 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -29,6 +31,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.IntentSender;
 import android.content.pm.PackageManager;
 import android.provider.Settings;
 import android.telephony.SubscriptionInfo;
@@ -624,22 +627,91 @@
     }
 
     /**
-     * Schedule to install the given package on next boot. The caller needs to
-     * ensure that the package must have been processed (uncrypt'd) if needed.
-     * It sets up the command in BCB (bootloader control block), which will
-     * be read by the bootloader and the recovery image.
+     * Prepare to apply an unattended update by asking the user for their Lock Screen Knowledge
+     * Factor (LSKF). If supplied, the {@code intentSender} will be called when the system is setup
+     * and ready to apply the OTA.
+     * <p>
+     * When the system is already prepared for update and this API is called again with the same
+     * {@code updateToken}, it will not call the intent sender nor request the user enter their Lock
+     * Screen Knowledge Factor.
+     * <p>
+     * When this API is called again with a different {@code updateToken}, the prepared-for-update
+     * status is reset and process repeats as though it's the initial call to this method as
+     * described in the first paragraph.
      *
-     * @param Context      the Context to use.
-     * @param packageFile  the package to be installed.
-     *
-     * @throws IOException if there were any errors setting up the BCB.
-     *
+     * @param context the Context to use.
+     * @param updateToken token used to indicate which update was prepared
+     * @param intentSender the intent to call when the update is prepared; may be {@code null}
+     * @throws IOException if there were any errors setting up unattended update
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.RECOVERY)
-    public static void scheduleUpdateOnBoot(Context context, File packageFile)
+    public static void prepareForUnattendedUpdate(@NonNull Context context,
+            @NonNull String updateToken, @Nullable IntentSender intentSender) throws IOException {
+        if (updateToken == null) {
+            throw new NullPointerException("updateToken == null");
+        }
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        if (!rs.requestLskf(updateToken, intentSender)) {
+            throw new IOException("preparation for update failed");
+        }
+    }
+
+    /**
+     * Request that any previously requested Lock Screen Knowledge Factor (LSKF) is cleared and
+     * the preparation for unattended update is reset.
+     *
+     * @param context the Context to use.
+     * @throws IOException if there were any errors setting up unattended update
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static boolean clearPrepareForUnattendedUpdate(@NonNull Context context)
             throws IOException {
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        return rs.clearLskf();
+    }
+
+    /**
+     * Request that the device reboot and apply the update that has been prepared. The
+     * {@code updateToken} must match what was given for {@link #prepareForUnattendedUpdate} or
+     * this will return {@code false}.
+     *
+     * @param context the Context to use.
+     * @param updateToken the token used to call {@link #prepareForUnattendedUpdate} before
+     * @param reason the reboot reason to give to the {@link PowerManager}
+     * @throws IOException if there were any errors setting up unattended update
+     * @return false if the reboot couldn't proceed because the device wasn't ready for an
+     *               unattended reboot or if the {@code updateToken} did not match the previously
+     *               given token
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static boolean rebootAndApply(@NonNull Context context, @NonNull String updateToken,
+            @NonNull String reason) throws IOException {
+        if (updateToken == null) {
+            throw new NullPointerException("updateToken == null");
+        }
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        return rs.rebootWithLskf(updateToken, reason);
+    }
+
+    /**
+     * Schedule to install the given package on next boot. The caller needs to ensure that the
+     * package must have been processed (uncrypt'd) if needed. It sets up the command in BCB
+     * (bootloader control block), which will be read by the bootloader and the recovery image.
+     *
+     * @param context the Context to use.
+     * @param packageFile the package to be installed.
+     * @throws IOException if there were any errors setting up the BCB.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.RECOVERY)
+    public static void scheduleUpdateOnBoot(Context context, File packageFile) throws IOException {
         String filename = packageFile.getCanonicalPath();
         boolean securityUpdate = filename.endsWith("_s.zip");
 
@@ -1204,6 +1276,49 @@
     }
 
     /**
+     * Begins the process of asking the user for the Lock Screen Knowledge Factor.
+     *
+     * @param updateToken token that will be used in calls to {@link #rebootAndApply} to ensure
+     *                    that the preparation was for the correct update
+     * @return true if the request was correct
+     * @throws IOException if the recovery system service could not be contacted
+     */
+    private boolean requestLskf(String updateToken, IntentSender sender) throws IOException {
+        try {
+            return mService.requestLskf(updateToken, sender);
+        } catch (RemoteException e) {
+            throw new IOException("could request update");
+        }
+    }
+
+    /**
+     * Calls the recovery system service and clears the setup for the OTA.
+     *
+     * @return true if the setup for OTA was cleared
+     * @throws IOException if the recovery system service could not be contacted
+     */
+    private boolean clearLskf() throws IOException {
+        try {
+            return mService.clearLskf();
+        } catch (RemoteException e) {
+            throw new IOException("could not clear LSKF");
+        }
+    }
+
+    /**
+     * Calls the recovery system service to reboot and apply update.
+     *
+     * @param updateToken the update token for which the update was prepared
+     */
+    private boolean rebootWithLskf(String updateToken, String reason) throws IOException {
+        try {
+            return mService.rebootWithLskf(updateToken, reason);
+        } catch (RemoteException e) {
+            throw new IOException("could not reboot for update");
+        }
+    }
+
+    /**
      * Internally, recovery treats each line of the command file as a separate
      * argv, so we only need to protect against newlines and nulls.
      */
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index f585c75..c1542c7 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -76,7 +76,8 @@
             return false;
         }
         try {
-            return mService.setAlwaysOnEffect(id, effect, attributes);
+            VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
+            return mService.setAlwaysOnEffect(id, effect, atr);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to set always-on effect.", e);
         }
@@ -91,7 +92,11 @@
             return;
         }
         try {
-            mService.vibrate(uid, opPkg, effect, attributes, reason, mToken);
+            if (attributes == null) {
+                attributes = new AudioAttributes.Builder().build();
+            }
+            VibrationAttributes atr = new VibrationAttributes.Builder(attributes, effect).build();
+            mService.vibrate(uid, opPkg, effect, atr, reason, mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fa9569b..6e199ce3 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1626,39 +1626,35 @@
     }
 
     /**
-     * Returns the calling user's user type.
+     * Returns whether the current user is of the given user type, such as
+     * {@link UserManager#USER_TYPE_FULL_GUEST}.
      *
-     * // TODO(b/142482943): Decide on the appropriate permission requirements.
-     *
-     * @return the name of the user type, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @return true if the user is of the given user type.
      * @hide
      */
-    public @NonNull String getUserType() {
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean isUserOfType(@NonNull String userType) {
         try {
-            return mService.getUserTypeForUser(UserHandle.myUserId());
+            return mService.isUserOfType(UserHandle.myUserId(), userType);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Returns the given user's user type.
-     *
-     * // TODO(b/142482943): Decide on the appropriate permission requirements.
-     * Requires {@link android.Manifest.permission#MANAGE_USERS} or
-     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission, otherwise the caller
-     * must be in the same profile group of specified user.
+     * Returns whether the given user is of the given user type, such as
+     * {@link UserManager#USER_TYPE_FULL_GUEST}.
      *
      * @param userHandle the user handle of the user whose type is being requested.
-     * @return the name of the user's user type, e.g. {@link UserManager#USER_TYPE_PROFILE_MANAGED},
-     *         or {@code null} if there is no such user.
+     * @param userType the name of the user's user type, e.g.
+     *                 {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
+     * @return true if the userHandle user is of type userType
      * @hide
      */
-    @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
-            android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
-    public @Nullable String getUserTypeForUser(@NonNull UserHandle userHandle) {
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public boolean isUserOfType(@NonNull UserHandle userHandle, @NonNull String userType) {
         try {
-            return mService.getUserTypeForUser(userHandle.getIdentifier());
+            return mService.isUserOfType(userHandle.getIdentifier(), userType);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
diff --git a/core/java/android/os/VibrationAttributes.aidl b/core/java/android/os/VibrationAttributes.aidl
new file mode 100644
index 0000000..5c05a4e
--- /dev/null
+++ b/core/java/android/os/VibrationAttributes.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2019, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.os;
+
+parcelable VibrationAttributes;
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
new file mode 100644
index 0000000..3e16640
--- /dev/null
+++ b/core/java/android/os/VibrationAttributes.java
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.media.AudioAttributes;
+import android.util.Slog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about a vibration
+ */
+public final class VibrationAttributes implements Parcelable {
+    private static final String TAG = "VibrationAttributes";
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "USAGE_CLASS_" }, value = {
+            USAGE_CLASS_UNKNOWN,
+            USAGE_CLASS_ALARM,
+            USAGE_CLASS_FEEDBACK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UsageClass{}
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "USAGE_" }, value = {
+            USAGE_UNKNOWN,
+            USAGE_ALARM,
+            USAGE_RINGTONE,
+            USAGE_NOTIFICATION,
+            USAGE_COMMUNICATION_REQUEST,
+            USAGE_TOUCH,
+            USAGE_PHYSICAL_EMULATION,
+            USAGE_HARDWARE_FEEDBACK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Usage{}
+
+    /**
+     * Vibration usage class value to use when the vibration usage class is unknown.
+     */
+    public static final int USAGE_CLASS_UNKNOWN = 0x0;
+    /**
+     * Vibration usage class value to use when the vibration is initiated to catch user's
+     * attention, such as alarm, ringtone, and notification vibrations.
+     */
+    public static final int USAGE_CLASS_ALARM = 0x1;
+    /**
+     * Vibration usage class value to use when the vibration is initiated as a response to user's
+     * actions, such as emulation of physical effects, and texting feedback vibration.
+     */
+    public static final int USAGE_CLASS_FEEDBACK = 0x2;
+
+    /**
+     * Mask for vibration usage class value.
+     */
+    public static final int USAGE_CLASS_MASK = 0xF;
+
+    /**
+     * Usage value to use when usage is unknown.
+     */
+    public static final int USAGE_UNKNOWN = 0x0 | USAGE_CLASS_UNKNOWN;
+    /**
+     * Usage value to use for alarm vibrations.
+     */
+    public static final int USAGE_ALARM = 0x10 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for ringtone vibrations.
+     */
+    public static final int USAGE_RINGTONE = 0x20 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for notification vibrations.
+     */
+    public static final int USAGE_NOTIFICATION = 0x30 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for vibrations which mean a request to enter/end a
+     * communication, such as a VoIP communication or video-conference.
+     */
+    public static final int USAGE_COMMUNICATION_REQUEST = 0x40 | USAGE_CLASS_ALARM;
+    /**
+     * Usage value to use for touch vibrations.
+     */
+    public static final int USAGE_TOUCH = 0x10 | USAGE_CLASS_FEEDBACK;
+    /**
+     * Usage value to use for vibrations which emulate physical effects, such as edge squeeze.
+     */
+    public static final int USAGE_PHYSICAL_EMULATION = 0x20 | USAGE_CLASS_FEEDBACK;
+    /**
+     * Usage value to use for vibrations which provide a feedback for hardware interaction,
+     * such as a fingerprint sensor.
+     */
+    public static final int USAGE_HARDWARE_FEEDBACK = 0x30 | USAGE_CLASS_FEEDBACK;
+
+    /**
+     * @hide
+     */
+    @IntDef(prefix = { "FLAG_" }, value = {
+            FLAG_BYPASS_INTERRUPTION_POLICY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flag{}
+
+    /**
+     * Flag requesting vibration effect to be played even under limited interruptions.
+     */
+    public static final int FLAG_BYPASS_INTERRUPTION_POLICY = 0x1;
+
+    // If a vibration is playing for longer than 5s, it's probably not haptic feedback
+    private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+
+    private final int mUsage;
+    private final int mFlags;
+
+    private final AudioAttributes mAudioAttributes;
+
+    private VibrationAttributes(int usage, int flags, @NonNull AudioAttributes audio) {
+        mUsage = usage;
+        mFlags = flags;
+        mAudioAttributes = audio;
+    }
+
+    /**
+     * Return the vibration usage class.
+     * @return USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK or USAGE_CLASS_UNKNOWN
+     */
+    public int getUsageClass() {
+        return mUsage & USAGE_CLASS_MASK;
+    }
+
+    /**
+     * Return the vibration usage.
+     * @return one of the values that can be set in {@link Builder#setUsage(int)}
+     */
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Return the flags.
+     * @return a combined mask of all flags
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Check whether a flag is set
+     * @return true if a flag is set and false otherwise
+     */
+    public boolean isFlagSet(int flag) {
+        return (mFlags & flag) > 0;
+    }
+
+    /**
+     * Return AudioAttributes equivalent to this VibrationAttributes.
+     * @deprecated Temporary support of AudioAttributes, will be removed when out of WIP
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    public @NonNull AudioAttributes getAudioAttributes() {
+        return mAudioAttributes;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mUsage);
+        dest.writeInt(mFlags);
+        dest.writeParcelable(mAudioAttributes, flags);
+    }
+
+    private VibrationAttributes(Parcel src) {
+        mUsage = src.readInt();
+        mFlags = src.readInt();
+        mAudioAttributes = (AudioAttributes) src.readParcelable(
+                AudioAttributes.class.getClassLoader());
+    }
+
+    public static final @NonNull Parcelable.Creator<VibrationAttributes>
+            CREATOR = new Parcelable.Creator<VibrationAttributes>() {
+                public VibrationAttributes createFromParcel(Parcel p) {
+                    return new VibrationAttributes(p);
+                }
+                public VibrationAttributes[] newArray(int size) {
+                    return new VibrationAttributes[size];
+                }
+            };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        VibrationAttributes rhs = (VibrationAttributes) o;
+        return mUsage == rhs.mUsage && mFlags == rhs.mFlags;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUsage, mFlags);
+    }
+
+    @Override
+    public String toString() {
+        return "VibrationAttributes:"
+                + " Usage=" + usageToString()
+                + " Flags=" + mFlags;
+    }
+
+    /** @hide */
+    public String usageToString() {
+        return usageToString(mUsage);
+    }
+
+    /** @hide */
+    public String usageToString(int usage) {
+        switch (usage) {
+            case USAGE_UNKNOWN:
+                return "UNKNOWN";
+            case USAGE_ALARM:
+                return "ALARM";
+            case USAGE_RINGTONE:
+                return "RIGNTONE";
+            case USAGE_NOTIFICATION:
+                return "NOTIFICATION";
+            case USAGE_COMMUNICATION_REQUEST:
+                return "COMMUNICATION_REQUEST";
+            case USAGE_TOUCH:
+                return "TOUCH";
+            case USAGE_PHYSICAL_EMULATION:
+                return "PHYSICAL_EMULATION";
+            case USAGE_HARDWARE_FEEDBACK:
+                return "HARDWARE_FEEDBACK";
+            default:
+                return "unknown usage " + usage;
+        }
+    }
+
+    /**
+     * Builder class for {@link VibrationAttributes} objects.
+     * By default, all information is set to UNKNOWN.
+     */
+    public static final class Builder {
+        private int mUsage = USAGE_UNKNOWN;
+        private int mFlags = 0x0;
+
+        private AudioAttributes mAudioAttributes = new AudioAttributes.Builder().build();
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given VibrationAttributes.
+         */
+        public Builder(@Nullable VibrationAttributes vib) {
+            if (vib != null) {
+                mUsage = vib.mUsage;
+                mFlags = vib.mFlags;
+                mAudioAttributes = vib.mAudioAttributes;
+            }
+        }
+
+        /**
+         * Constructs a new Builder from AudioAttributes.
+         * @hide
+         */
+        @TestApi
+        public Builder(@NonNull AudioAttributes audio,
+                @Nullable VibrationEffect effect) {
+            mAudioAttributes = audio;
+            setUsage(audio);
+            applyHapticFeedbackHeuristics(effect);
+        }
+
+        private void applyHapticFeedbackHeuristics(@Nullable VibrationEffect effect) {
+            if (effect != null) {
+                if (mUsage == USAGE_UNKNOWN && effect instanceof VibrationEffect.Prebaked) {
+                    VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
+                    switch (prebaked.getId()) {
+                        case VibrationEffect.EFFECT_CLICK:
+                        case VibrationEffect.EFFECT_DOUBLE_CLICK:
+                        case VibrationEffect.EFFECT_HEAVY_CLICK:
+                        case VibrationEffect.EFFECT_TEXTURE_TICK:
+                        case VibrationEffect.EFFECT_TICK:
+                        case VibrationEffect.EFFECT_POP:
+                        case VibrationEffect.EFFECT_THUD:
+                            mUsage = USAGE_TOUCH;
+                            break;
+                        default:
+                            Slog.w(TAG, "Unknown prebaked vibration effect, assuming it isn't "
+                                    + "haptic feedback");
+                    }
+                }
+                final long duration = effect.getDuration();
+                if (mUsage == USAGE_UNKNOWN && duration >= 0
+                        && duration < MAX_HAPTIC_FEEDBACK_DURATION) {
+                    mUsage = USAGE_TOUCH;
+                }
+            }
+        }
+
+        private void setUsage(@NonNull AudioAttributes audio) {
+            switch (audio.getUsage()) {
+                case AudioAttributes.USAGE_NOTIFICATION:
+                case AudioAttributes.USAGE_NOTIFICATION_EVENT:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+                    mUsage = USAGE_NOTIFICATION;
+                    break;
+                case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+                case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
+                    mUsage = USAGE_COMMUNICATION_REQUEST;
+                    break;
+                case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
+                    mUsage = USAGE_RINGTONE;
+                    break;
+                case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
+                    mUsage = USAGE_TOUCH;
+                    break;
+                case AudioAttributes.USAGE_ALARM:
+                    mUsage = USAGE_ALARM;
+                    break;
+                default:
+                    mUsage = USAGE_UNKNOWN;
+            }
+        }
+
+        /**
+         * Combines all of the attributes that have been set and returns a new
+         * {@link VibrationAttributes} object.
+         * @return a new {@link VibrationAttributes} object
+         */
+        public @NonNull VibrationAttributes build() {
+            VibrationAttributes ans = new VibrationAttributes(mUsage, mFlags,
+                    mAudioAttributes);
+            return ans;
+        }
+
+        /**
+         * Sets the attribute describing the type of corresponding vibration.
+         * @param usage one of {@link VibrationAttributes#USAGE_ALARM},
+         * {@link VibrationAttributes#USAGE_RINGTONE},
+         * {@link VibrationAttributes#USAGE_NOTIFICATION},
+         * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST},
+         * {@link VibrationAttributes#USAGE_TOUCH},
+         * {@link VibrationAttributes#USAGE_PHYSICAL_EMULATION},
+         * {@link VibrationAttributes#USAGE_HARDWARE_FEEDBACK}.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setUsage(int usage) {
+            mUsage = usage;
+            return this;
+        }
+
+        /**
+         * Replaces flags
+         * @param flags any combination of flags.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder replaceFlags(int flags) {
+            mFlags = flags;
+            return this;
+        }
+    }
+}
+
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 2138d553..63335a0 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -38,6 +38,7 @@
 import android.content.pm.DataLoaderParams;
 import android.content.pm.InstallationFile;
 import android.os.IVold;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.ArraySet;
@@ -208,7 +209,8 @@
         if (!hasObb()) {
             return;
         }
-        final String mainObbDir = String.format("/storage/emulated/0/Android/obb/%s", mPackageName);
+        final String obbDir = "/storage/emulated/0/Android/obb";
+        final String packageObbDir = String.format("%s/%s", obbDir, mPackageName);
         final String packageObbDirRoot =
                 String.format("/mnt/runtime/%s/emulated/0/Android/obb/", mPackageName);
         final String[] obbDirs = {
@@ -217,12 +219,12 @@
                 packageObbDirRoot + "full",
                 packageObbDirRoot + "default",
                 String.format("/data/media/0/Android/obb/%s", mPackageName),
-                mainObbDir,
+                packageObbDir,
         };
         try {
-            Slog.i(TAG, "Creating obb directory '" + mainObbDir + "'");
+            Slog.i(TAG, "Creating obb directory '" + packageObbDir + "'");
             final IVold vold = IVold.Stub.asInterface(ServiceManager.getServiceOrThrow("vold"));
-            vold.mkdirs(mainObbDir);
+            vold.setupAppDir(packageObbDir, obbDir, Process.ROOT_UID);
             for (String d : obbDirs) {
                 mObbStorage.bindPermanent(d);
             }
@@ -230,7 +232,7 @@
             Slog.e(TAG, "vold service is not found.");
             cleanUp();
         } catch (IOException | RemoteException ex) {
-            Slog.e(TAG, "Failed to create obb dir at: " + mainObbDir, ex);
+            Slog.e(TAG, "Failed to create obb dir at: " + packageObbDir, ex);
             cleanUp();
         }
     }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index a3215a4..2583292 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -318,8 +318,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
-            Manifest.permission.PACKAGE_USAGE_STATS})
+    @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
     public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis,
             @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
             @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
@@ -340,8 +339,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(allOf = {Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
-            Manifest.permission.PACKAGE_USAGE_STATS})
+    @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
     public void stopOneTimePermissionSession(@NonNull String packageName) {
         try {
             mPermissionManager.stopOneTimePermissionSession(packageName,
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
index 69c4b9b..aa2a89c 100644
--- a/core/java/android/provider/ContactsInternal.java
+++ b/core/java/android/provider/ContactsInternal.java
@@ -15,15 +15,14 @@
  */
 package android.provider;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ActivityNotFoundException;
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
 import android.content.UriMatcher;
 import android.net.Uri;
-import android.os.Process;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.widget.Toast;
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index eb09930..0a3c333 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -22,7 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentInterface;
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/core/java/android/provider/Downloads.java b/core/java/android/provider/Downloads.java
index 9a384c6..48410a7 100644
--- a/core/java/android/provider/Downloads.java
+++ b/core/java/android/provider/Downloads.java
@@ -16,8 +16,8 @@
 
 package android.provider;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.DownloadManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.NetworkPolicyManager;
 import android.net.Uri;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3aa534e..bbaf94a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -26,7 +26,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
@@ -36,6 +35,7 @@
 import android.app.NotificationManager;
 import android.app.SearchManager;
 import android.app.WallpaperManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -252,6 +252,9 @@
     /** @hide */
     public static final String EXTRA_NETWORK_TEMPLATE = "network_template";
 
+    /** @hide */
+    public static final String KEY_CONFIG_SET_RETURN = "config_set_return";
+
     /**
      * An int extra specifying a subscription ID.
      *
@@ -2504,13 +2507,14 @@
                 args.putString(CALL_METHOD_PREFIX_KEY, prefix);
                 args.putSerializable(CALL_METHOD_FLAGS_KEY, keyValues);
                 IContentProvider cp = mProviderHolder.getProvider(cr);
-                cp.call(cr.getPackageName(), cr.getFeatureId(), mProviderHolder.mUri.getAuthority(),
+                Bundle bundle = cp.call(cr.getPackageName(), cr.getFeatureId(),
+                        mProviderHolder.mUri.getAuthority(),
                         mCallSetAllCommand, null, args);
+                return bundle.getBoolean(KEY_CONFIG_SET_RETURN);
             } catch (RemoteException e) {
                 // Not supported by the remote side
                 return false;
             }
-            return true;
         }
 
         @UnsupportedAppUsage
@@ -13709,14 +13713,6 @@
                 "backup_agent_timeout_parameters";
 
         /**
-         * Whether the backup system service supports multiple users (0 = disabled, 1 = enabled). If
-         * disabled, the service will only be active for the system user.
-         *
-         * @hide
-         */
-        public static final String BACKUP_MULTI_USER_ENABLED = "backup_multi_user_enabled";
-
-        /**
          * Blacklist of GNSS satellites.
          *
          * This is a list of integers separated by commas to represent pairs of (constellation,
@@ -13985,14 +13981,18 @@
          */
         @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
         static boolean setStrings(@NonNull ContentResolver resolver, @NonNull String namespace,
-                @NonNull Map<String, String> keyValues) {
+                @NonNull Map<String, String> keyValues) throws DeviceConfig.BadConfigException {
             HashMap<String, String> compositeKeyValueMap = new HashMap<>(keyValues.keySet().size());
             for (Map.Entry<String, String> entry : keyValues.entrySet()) {
                 compositeKeyValueMap.put(
                         createCompositeName(namespace, entry.getKey()), entry.getValue());
             }
-            return sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
-                    compositeKeyValueMap);
+            // If can't set given configuration that means it's bad
+            if (!sNameValueCache.setStringsForPrefix(resolver, createPrefix(namespace),
+                    compositeKeyValueMap)) {
+                throw new DeviceConfig.BadConfigException();
+            }
+            return true;
         }
 
         /**
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index fed3d7ba..1d9bdb8 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -24,8 +24,8 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.compat.annotation.ChangeId;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -36,6 +36,7 @@
 import android.database.sqlite.SqliteWrapper;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -3559,7 +3560,8 @@
          * can manage DPC-owned APNs.
          * @hide
          */
-        public static final Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
+        @SystemApi
+        public static final @NonNull Uri DPC_URI = Uri.parse("content://telephony/carriers/dpc");
 
         /**
          * The {@code content://} style URL to be called from Telephony to query APNs.
@@ -3868,6 +3870,13 @@
         public static final String USER_EDITABLE = "user_editable";
 
         /**
+         * Integer value denoting an invalid APN id
+         * @hide
+         */
+        @SystemApi
+        public static final int INVALID_APN_ID = -1;
+
+        /**
          * {@link #EDITED_STATUS APN edit status} indicates that this APN has not been edited or
          * fails to edit.
          * <p>Type: INTEGER </p>
@@ -4097,10 +4106,117 @@
         public static final Uri MESSAGE_HISTORY_URI = Uri.parse("content://cellbroadcasts/history");
 
         /**
+         * The authority for the legacy cellbroadcast provider.
+         * This is used for OEM data migration. OEMs want to migrate message history or
+         * sharepreference data to mainlined cellbroadcastreceiver app, should have a
+         * contentprovider with authority: cellbroadcast-legacy. Mainlined cellbroadcastreceiver
+         * will interact with this URI to retrieve data and persists to mainlined cellbroadcast app.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final @NonNull String AUTHORITY_LEGACY = "cellbroadcast-legacy";
+
+        /**
+         * A content:// style uri to the authority for the legacy cellbroadcast provider.
+         * @hide
+         */
+        @SystemApi
+        public static final @NonNull Uri AUTHORITY_LEGACY_URI =
+                Uri.parse("content://cellbroadcast-legacy");
+
+        /**
+         * Method name to {@link android.content.ContentProvider#call(String, String, Bundle)
+         * for {@link #AUTHORITY_LEGACY}. Used to query cellbroadcast {@link Preference},
+         * containing following supported entries
+         * <ul>
+         *     <li>{@link #ENABLE_AREA_UPDATE_INFO_PREF}</li>
+         *     <li>{@link #ENABLE_TEST_ALERT_PREF}</li>
+         *     <li>{@link #ENABLE_STATE_LOCAL_TEST_PREF}</li>
+         *     <li>{@link #ENABLE_PUBLIC_SAFETY_PREF}</li>
+         *     <li>{@link #ENABLE_CMAS_AMBER_PREF}</li>
+         *     <li>{@link #ENABLE_CMAS_SEVERE_THREAT_PREF}</li>
+         *     <li>{@link #ENABLE_CMAS_EXTREME_THREAT_PREF}</li>
+         *     <li>{@link #ENABLE_CMAS_PRESIDENTIAL_PREF}</li>
+         *     <li>{@link #ENABLE_ALERT_VIBRATION_PREF}</li>
+         *     <li>{@link #ENABLE_EMERGENCY_PERF}</li>
+         *     <li>{@link #ENABLE_FULL_VOLUME_PREF}</li>
+         *     <li>{@link #ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF}</li>
+         * </ul>
+         * @hide
+         */
+        @SystemApi
+        public static final @NonNull String CALL_METHOD_GET_PREFERENCE = "get_preference";
+
+        /**
+         * Arg name to {@link android.content.ContentProvider#call(String, String, Bundle)}
+         * for {@link #AUTHORITY_LEGACY}.
+         * Contains all supported shared preferences for cellbroadcast.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final class Preference {
+            /**
+             * Not Instantiatable.
+             * @hide
+             */
+            private Preference() {}
+
+            /** Preference to enable area update info alert */
+            public static final @NonNull String ENABLE_AREA_UPDATE_INFO_PREF =
+                    "enable_area_update_info_alerts";
+
+            /** Preference to enable test alert */
+            public static final @NonNull String ENABLE_TEST_ALERT_PREF =
+                    "enable_test_alerts";
+
+            /** Preference to enable state local test alert */
+            public static final @NonNull String ENABLE_STATE_LOCAL_TEST_PREF
+                    = "enable_state_local_test_alerts";
+
+            /** Preference to enable public safety alert */
+            public static final @NonNull String ENABLE_PUBLIC_SAFETY_PREF
+                    = "enable_public_safety_messages";
+
+            /** Preference to enable amber alert */
+            public static final @NonNull String ENABLE_CMAS_AMBER_PREF
+                    = "enable_cmas_amber_alerts";
+
+            /** Preference to enable severe threat alert */
+            public static final @NonNull String ENABLE_CMAS_SEVERE_THREAT_PREF
+                    = "enable_cmas_severe_threat_alerts";
+
+            /** Preference to enable extreme threat alert */
+            public static final @NonNull String ENABLE_CMAS_EXTREME_THREAT_PREF =
+                    "enable_cmas_extreme_threat_alerts";
+
+            /** Preference to enable presidential alert */
+            public static final @NonNull String ENABLE_CMAS_PRESIDENTIAL_PREF =
+                    "enable_cmas_presidential_alerts";
+
+            /** Preference to enable alert vibration */
+            public static final @NonNull String ENABLE_ALERT_VIBRATION_PREF =
+                    "enable_alert_vibrate";
+
+            /** Preference to enable emergency alert */
+            public static final @NonNull String ENABLE_EMERGENCY_PERF =
+                    "enable_emergency_alerts";
+
+            /** Preference to enable volume for alerts */
+            public static final @NonNull String ENABLE_FULL_VOLUME_PREF =
+                    "use_full_volume";
+
+            /** Preference to enable receive alerts in second language */
+            public static final @NonNull String ENABLE_CMAS_IN_SECOND_LANGUAGE_PREF =
+                    "receive_cmas_in_second_language";
+        }
+
+        /**
          * The subscription which received this cell broadcast message.
          * <P>Type: INTEGER</P>
          */
-        public static final String SUB_ID = "sub_id";
+        public static final String SUBSCRIPTION_ID = "sub_id";
 
         /**
          * The slot which received this cell broadcast message.
@@ -4358,7 +4474,7 @@
         public static final String[] QUERY_COLUMNS_FWK = {
                 _ID,
                 SLOT_INDEX,
-                SUB_ID,
+                SUBSCRIPTION_ID,
                 GEOGRAPHICAL_SCOPE,
                 PLMN,
                 LAC,
diff --git a/core/java/android/security/KeystoreArguments.java b/core/java/android/security/KeystoreArguments.java
index e634234..a59c4e0 100644
--- a/core/java/android/security/KeystoreArguments.java
+++ b/core/java/android/security/KeystoreArguments.java
@@ -16,7 +16,7 @@
 
 package android.security;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/security/keymaster/ExportResult.java b/core/java/android/security/keymaster/ExportResult.java
index 1be5ae9..037b852 100644
--- a/core/java/android/security/keymaster/ExportResult.java
+++ b/core/java/android/security/keymaster/ExportResult.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index 2eb2cef..d8382fa 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/security/keymaster/KeymasterArguments.java b/core/java/android/security/keymaster/KeymasterArguments.java
index d2dbdec..e009e12 100644
--- a/core/java/android/security/keymaster/KeymasterArguments.java
+++ b/core/java/android/security/keymaster/KeymasterArguments.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java
index 6a2024f..68365bf 100644
--- a/core/java/android/security/keymaster/KeymasterBlob.java
+++ b/core/java/android/security/keymaster/KeymasterBlob.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java
index fc562bd..81b08c5 100644
--- a/core/java/android/security/keymaster/KeymasterBlobArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 
 /**
diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
index 4286aa0..25b2ac4 100644
--- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 
 /**
diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java
index 3e04c15..218f488 100644
--- a/core/java/android/security/keymaster/KeymasterDateArgument.java
+++ b/core/java/android/security/keymaster/KeymasterDateArgument.java
@@ -16,8 +16,9 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
+
 import java.util.Date;
 
 /**
diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java
index 4aadce4..01d38c7 100644
--- a/core/java/android/security/keymaster/KeymasterIntArgument.java
+++ b/core/java/android/security/keymaster/KeymasterIntArgument.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 
 /**
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index bc2255e..3ac27cc 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 
 /**
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index c278eb3..b4e155a 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -16,7 +16,7 @@
 
 package android.security.keymaster;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
index d8936d9..58dc4ba 100644
--- a/core/java/android/security/net/config/RootTrustManager.java
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -16,15 +16,16 @@
 
 package android.security.net.config;
 
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.net.Socket;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.List;
 
-import android.annotation.UnsupportedAppUsage;
-import javax.net.ssl.SSLSocket;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
 import javax.net.ssl.X509ExtendedTrustManager;
 
 /**
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index b7ec281..fbc25a6 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -80,6 +80,8 @@
     @Nullable
     private final ArrayMap<String, Long> mCompatibilityPackages;
 
+    private final boolean mInlineSuggestionsEnabled;
+
     public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
         this(context, getServiceInfoOrThrow(comp, userHandle));
@@ -112,11 +114,13 @@
         if (parser == null) {
             mSettingsActivity = null;
             mCompatibilityPackages = null;
+            mInlineSuggestionsEnabled = false;
             return;
         }
 
         String settingsActivity = null;
         ArrayMap<String, Long> compatibilityPackages = null;
+        boolean inlineSuggestionsEnabled = false; // false by default.
 
         try {
             final Resources resources = context.getPackageManager().getResourcesForApplication(
@@ -135,6 +139,8 @@
                             com.android.internal.R.styleable.AutofillService);
                     settingsActivity = afsAttributes.getString(
                             R.styleable.AutofillService_settingsActivity);
+                    inlineSuggestionsEnabled = afsAttributes.getBoolean(
+                            R.styleable.AutofillService_supportsInlineSuggestions, false);
                 } finally {
                     if (afsAttributes != null) {
                         afsAttributes.recycle();
@@ -150,6 +156,7 @@
 
         mSettingsActivity = settingsActivity;
         mCompatibilityPackages = compatibilityPackages;
+        mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
     }
 
     private ArrayMap<String, Long> parseCompatibilityPackages(XmlPullParser parser,
@@ -227,6 +234,10 @@
         return mCompatibilityPackages;
     }
 
+    public boolean isInlineSuggestionsEnabled() {
+        return mInlineSuggestionsEnabled;
+    }
+
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
@@ -235,6 +246,7 @@
         builder.append(", settings:").append(mSettingsActivity);
         builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null
                 && !mCompatibilityPackages.isEmpty()).append("]");
+        builder.append(", inline suggestions enabled:").append(mInlineSuggestionsEnabled);
         return builder.toString();
     }
 
@@ -245,5 +257,7 @@
         pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName());
         pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity);
         pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages);
+        pw.print(prefix); pw.print("Inline Suggestions Enabled: ");
+        pw.println(mInlineSuggestionsEnabled);
     }
 }
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index d53e62a..d827b30 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -56,6 +57,10 @@
  * all datasets pairs that have that view's {@link AutofillId}. Then, when the user selects a
  * dataset from the UI, all views in that dataset are autofilled.
  *
+ * <p>If both the current Input Method and autofill service supports inline suggestions, the Dataset
+ * can be shown by the keyboard as a suggestion. To use this feature, the Dataset should contain
+ * an {@link InlinePresentation} representing how the inline suggestion UI will be rendered.
+ *
  * <a name="Authentication"></a>
  * <h3>Dataset authentication</h3>
  *
@@ -99,8 +104,10 @@
     private final ArrayList<AutofillId> mFieldIds;
     private final ArrayList<AutofillValue> mFieldValues;
     private final ArrayList<RemoteViews> mFieldPresentations;
+    private final ArrayList<InlinePresentation> mFieldInlinePresentations;
     private final ArrayList<DatasetFieldFilter> mFieldFilters;
     private final RemoteViews mPresentation;
+    @Nullable private final InlinePresentation mInlinePresentation;
     private final IntentSender mAuthentication;
     @Nullable String mId;
 
@@ -108,8 +115,10 @@
         mFieldIds = builder.mFieldIds;
         mFieldValues = builder.mFieldValues;
         mFieldPresentations = builder.mFieldPresentations;
+        mFieldInlinePresentations = builder.mFieldInlinePresentations;
         mFieldFilters = builder.mFieldFilters;
         mPresentation = builder.mPresentation;
+        mInlinePresentation = builder.mInlinePresentation;
         mAuthentication = builder.mAuthentication;
         mId = builder.mId;
     }
@@ -132,6 +141,13 @@
 
     /** @hide */
     @Nullable
+    public InlinePresentation getFieldInlinePresentation(int index) {
+        final InlinePresentation inlinePresentation = mFieldInlinePresentations.get(index);
+        return inlinePresentation != null ? inlinePresentation : mInlinePresentation;
+    }
+
+    /** @hide */
+    @Nullable
     public DatasetFieldFilter getFilter(int index) {
         return mFieldFilters.get(index);
     }
@@ -165,7 +181,9 @@
         }
         if (mFieldPresentations != null) {
             builder.append(", fieldPresentations=").append(mFieldPresentations.size());
-
+        }
+        if (mFieldInlinePresentations != null) {
+            builder.append(", fieldInlinePresentations=").append(mFieldInlinePresentations.size());
         }
         if (mFieldFilters != null) {
             builder.append(", fieldFilters=").append(mFieldFilters.size());
@@ -173,6 +191,9 @@
         if (mPresentation != null) {
             builder.append(", hasPresentation");
         }
+        if (mInlinePresentation != null) {
+            builder.append(", hasInlinePresentation");
+        }
         if (mAuthentication != null) {
             builder.append(", hasAuthentication");
         }
@@ -198,8 +219,10 @@
         private ArrayList<AutofillId> mFieldIds;
         private ArrayList<AutofillValue> mFieldValues;
         private ArrayList<RemoteViews> mFieldPresentations;
+        private ArrayList<InlinePresentation> mFieldInlinePresentations;
         private ArrayList<DatasetFieldFilter> mFieldFilters;
         private RemoteViews mPresentation;
+        @Nullable private InlinePresentation mInlinePresentation;
         private IntentSender mAuthentication;
         private boolean mDestroyed;
         @Nullable private String mId;
@@ -208,10 +231,39 @@
          * Creates a new builder.
          *
          * @param presentation The presentation used to visualize this dataset.
+         * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+         *              as inline suggestions. If the dataset supports inline suggestions,
+         *              this should not be null.
          */
-        public Builder(@NonNull RemoteViews presentation) {
+        public Builder(@NonNull RemoteViews presentation,
+                @NonNull InlinePresentation inlinePresentation) {
             Preconditions.checkNotNull(presentation, "presentation must be non-null");
             mPresentation = presentation;
+            mInlinePresentation = inlinePresentation;
+        }
+
+        /**
+         * Creates a new builder.
+         *
+         * @param presentation The presentation used to visualize this dataset.
+         */
+        public Builder(@NonNull RemoteViews presentation) {
+            this(presentation, null);
+        }
+
+        /**
+         * Creates a new builder.
+         *
+         * <p>Only called by augmented autofill.
+         *
+         * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+         *              as inline suggestions. If the dataset supports inline suggestions,
+         *              this should not be null.
+         * @hide
+         */
+        @SystemApi
+        public Builder(@NonNull InlinePresentation inlinePresentation) {
+            mInlinePresentation = inlinePresentation;
         }
 
         /**
@@ -325,7 +377,7 @@
          */
         public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value) {
             throwIfDestroyed();
-            setLifeTheUniverseAndEverything(id, value, null, null);
+            setLifeTheUniverseAndEverything(id, value, null, null, null);
             return this;
         }
 
@@ -353,7 +405,7 @@
                 @NonNull RemoteViews presentation) {
             throwIfDestroyed();
             Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation, null);
+            setLifeTheUniverseAndEverything(id, value, presentation, null, null);
             return this;
         }
 
@@ -389,7 +441,7 @@
             throwIfDestroyed();
             Preconditions.checkState(mPresentation != null,
                     "Dataset presentation not set on constructor");
-            setLifeTheUniverseAndEverything(id, value, null, new DatasetFieldFilter(filter));
+            setLifeTheUniverseAndEverything(id, value, null, null, new DatasetFieldFilter(filter));
             return this;
         }
 
@@ -424,13 +476,119 @@
                 @Nullable Pattern filter, @NonNull RemoteViews presentation) {
             throwIfDestroyed();
             Preconditions.checkNotNull(presentation, "presentation cannot be null");
-            setLifeTheUniverseAndEverything(id, value, presentation,
+            setLifeTheUniverseAndEverything(id, value, presentation, null,
+                    new DatasetFieldFilter(filter));
+            return this;
+        }
+
+        /**
+         * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+         * visualize it and an {@link InlinePresentation} to visualize it as an inline suggestion.
+         *
+         * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
+         * value it's easier to filter by calling
+         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
+         *
+         * @param id id returned by {@link
+         *        android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+         *        but the target view is a logical part of the dataset. For example, if
+         *        the dataset needs authentication and you have no access to the value.
+         * @param presentation the presentation used to visualize this field.
+         * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+         *        as inline suggestions. If the dataset supports inline suggestions,
+         *        this should not be null.
+         *
+         * @return this builder.
+         */
+        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+                @NonNull RemoteViews presentation, @NonNull InlinePresentation inlinePresentation) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(presentation, "presentation cannot be null");
+            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+            setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation, null);
+            return this;
+        }
+
+        /**
+         * Sets the value of a field, using a custom {@link RemoteViews presentation} to
+         * visualize it and a <a href="#Filtering">explicit filter</a>, and an
+         * {@link InlinePresentation} to visualize it as an inline suggestion.
+         *
+         * <p>This method is typically used when the dataset requires authentication and the service
+         * does not know its value but wants to hide the dataset after the user enters a minimum
+         * number of characters. For example, if the dataset represents a credit card number and the
+         * service does not want to show the "Tap to authenticate" message until the user tapped
+         * 4 digits, in which case the filter would be {@code Pattern.compile("\\d.{4,}")}.
+         *
+         * <p><b>Note:</b> If the dataset requires authentication but the service knows its text
+         * value it's easier to filter by calling
+         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} and using the value to filter.
+         *
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+         *        but the target view is a logical part of the dataset. For example, if
+         *        the dataset needs authentication and you have no access to the value.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+         *        when {@code null}, it disables filtering on that dataset (this is the recommended
+         *        approach when {@code value} is not {@code null} and field contains sensitive data
+         *        such as passwords).
+         * @param presentation the presentation used to visualize this field.
+         * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+         *        as inline suggestions. If the dataset supports inline suggestions, this
+         *        should not be null.
+         *
+         * @return this builder.
+         */
+        public @NonNull Builder setValue(@NonNull AutofillId id, @Nullable AutofillValue value,
+                @Nullable Pattern filter, @NonNull RemoteViews presentation,
+                @NonNull InlinePresentation inlinePresentation) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(presentation, "presentation cannot be null");
+            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+            setLifeTheUniverseAndEverything(id, value, presentation, inlinePresentation,
+                    new DatasetFieldFilter(filter));
+            return this;
+        }
+
+        /**
+         * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an
+         * {@link InlinePresentation} to visualize it as an inline suggestion.
+         *
+         * <p>Only called by augmented autofill.
+         *
+         * @param id id returned by {@link
+         *         android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
+         * @param value the value to be autofilled. Pass {@code null} if you do not have the value
+         *        but the target view is a logical part of the dataset. For example, if
+         *        the dataset needs authentication and you have no access to the value.
+         * @param filter regex used to determine if the dataset should be shown in the autofill UI;
+         *        when {@code null}, it disables filtering on that dataset (this is the recommended
+         *        approach when {@code value} is not {@code null} and field contains sensitive data
+         *        such as passwords).
+         * @param inlinePresentation The {@link InlinePresentation} used to visualize this dataset
+         *        as inline suggestions. If the dataset supports inline suggestions, this
+         *        should not be null.
+         *
+         * @return this builder.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setInlinePresentation(@NonNull AutofillId id,
+                @Nullable AutofillValue value, @Nullable Pattern filter,
+                @NonNull InlinePresentation inlinePresentation) {
+            throwIfDestroyed();
+            Preconditions.checkNotNull(inlinePresentation, "inlinePresentation cannot be null");
+            setLifeTheUniverseAndEverything(id, value, null, inlinePresentation,
                     new DatasetFieldFilter(filter));
             return this;
         }
 
         private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
+                @Nullable InlinePresentation inlinePresentation,
                 @Nullable DatasetFieldFilter filter) {
             Preconditions.checkNotNull(id, "id cannot be null");
             if (mFieldIds != null) {
@@ -438,6 +596,7 @@
                 if (existingIdx >= 0) {
                     mFieldValues.set(existingIdx, value);
                     mFieldPresentations.set(existingIdx, presentation);
+                    mFieldInlinePresentations.set(existingIdx, inlinePresentation);
                     mFieldFilters.set(existingIdx, filter);
                     return;
                 }
@@ -445,11 +604,13 @@
                 mFieldIds = new ArrayList<>();
                 mFieldValues = new ArrayList<>();
                 mFieldPresentations = new ArrayList<>();
+                mFieldInlinePresentations = new ArrayList<>();
                 mFieldFilters = new ArrayList<>();
             }
             mFieldIds.add(id);
             mFieldValues.add(value);
             mFieldPresentations.add(presentation);
+            mFieldInlinePresentations.add(inlinePresentation);
             mFieldFilters.add(filter);
         }
 
@@ -460,7 +621,8 @@
          *
          * @throws IllegalStateException if no field was set (through
          * {@link #setValue(AutofillId, AutofillValue)} or
-         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)}).
+         * {@link #setValue(AutofillId, AutofillValue, RemoteViews)} or
+         * {@link #setValue(AutofillId, AutofillValue, RemoteViews, InlinePresentation)}).
          *
          * @return The built dataset.
          */
@@ -492,38 +654,49 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeParcelable(mPresentation, flags);
+        parcel.writeParcelable(mInlinePresentation, flags);
         parcel.writeTypedList(mFieldIds, flags);
         parcel.writeTypedList(mFieldValues, flags);
         parcel.writeTypedList(mFieldPresentations, flags);
+        parcel.writeTypedList(mFieldInlinePresentations, flags);
         parcel.writeTypedList(mFieldFilters, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeString(mId);
     }
 
-    public static final @android.annotation.NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
+    public static final @NonNull Creator<Dataset> CREATOR = new Creator<Dataset>() {
         @Override
         public Dataset createFromParcel(Parcel parcel) {
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
             final RemoteViews presentation = parcel.readParcelable(null);
-            final Builder builder = (presentation == null)
-                    ? new Builder()
-                    : new Builder(presentation);
+            final InlinePresentation inlinePresentation = parcel.readParcelable(null);
+            final Builder builder = presentation == null
+                    ? new Builder(inlinePresentation)
+                    : inlinePresentation == null
+                            ? new Builder(presentation)
+                            : new Builder(presentation, inlinePresentation);
             final ArrayList<AutofillId> ids =
                     parcel.createTypedArrayList(AutofillId.CREATOR);
             final ArrayList<AutofillValue> values =
                     parcel.createTypedArrayList(AutofillValue.CREATOR);
             final ArrayList<RemoteViews> presentations =
                     parcel.createTypedArrayList(RemoteViews.CREATOR);
+            final ArrayList<InlinePresentation> inlinePresentations =
+                    parcel.createTypedArrayList(InlinePresentation.CREATOR);
             final ArrayList<DatasetFieldFilter> filters =
                     parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+            final int inlinePresentationsSize = inlinePresentations.size();
             for (int i = 0; i < ids.size(); i++) {
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = values.get(i);
                 final RemoteViews fieldPresentation = presentations.get(i);
+                final InlinePresentation fieldInlinePresentation =
+                        i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
                 final DatasetFieldFilter filter = filters.get(i);
-                builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation, filter);
+                builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
+                        fieldInlinePresentation, filter);
             }
             builder.setAuthentication(parcel.readParcelable(null));
             builder.setId(parcel.readString());
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 02a6390..d51e4ca 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -25,7 +25,6 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.app.Activity;
-import android.app.slice.Slice;
 import android.content.IntentSender;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
@@ -87,7 +86,7 @@
     private int mRequestId;
     private final @Nullable UserData mUserData;
     private final @Nullable int[] mCancelIds;
-    private final @Nullable ParceledListSlice<Slice> mInlineSuggestionSlices;
+    private final boolean mSupportsInlineSuggestions;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -105,8 +104,7 @@
         mRequestId = INVALID_REQUEST_ID;
         mUserData = builder.mUserData;
         mCancelIds = builder.mCancelIds;
-        mInlineSuggestionSlices = (builder.mInlineSuggestionSlices != null)
-                ? new ParceledListSlice<>(builder.mInlineSuggestionSlices) : null;
+        mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
     }
 
     /** @hide */
@@ -200,8 +198,8 @@
     }
 
     /** @hide */
-    public List<Slice> getInlineSuggestionSlices() {
-        return (mInlineSuggestionSlices != null) ? mInlineSuggestionSlices.getList() : null;
+    public boolean supportsInlineSuggestions() {
+        return mSupportsInlineSuggestions;
     }
 
     /**
@@ -224,7 +222,7 @@
         private boolean mDestroyed;
         private UserData mUserData;
         private int[] mCancelIds;
-        private ArrayList<Slice> mInlineSuggestionSlices;
+        private boolean mSupportsInlineSuggestions;
 
         /**
          * Triggers a custom UI before before autofilling the screen with any data set in this
@@ -580,20 +578,6 @@
         }
 
         /**
-         * TODO(b/137800469): add javadoc
-         */
-        @NonNull
-        public Builder addInlineSuggestionSlice(@NonNull Slice inlineSuggestionSlice) {
-            throwIfDestroyed();
-            throwIfAuthenticationCalled();
-            if (mInlineSuggestionSlices == null) {
-                mInlineSuggestionSlices = new ArrayList<>();
-            }
-            mInlineSuggestionSlices.add(inlineSuggestionSlice);
-            return this;
-        }
-
-        /**
          * Builds a new {@link FillResponse} instance.
          *
          * @throws IllegalStateException if any of the following conditions occur:
@@ -624,6 +608,14 @@
                 throw new IllegalStateException(
                         "must add at least 1 dataset when using header or footer");
             }
+
+            for (final Dataset dataset : mDatasets) {
+                if (dataset.getFieldInlinePresentation(0) != null) {
+                    mSupportsInlineSuggestions = true;
+                    break;
+                }
+            }
+
             mDestroyed = true;
             return new FillResponse(this);
         }
@@ -694,9 +686,6 @@
         if (mCancelIds != null) {
             builder.append(", mCancelIds=").append(mCancelIds.length);
         }
-        if (mInlineSuggestionSlices != null) {
-            builder.append(", inlinedSuggestions=").append(mInlineSuggestionSlices.getList());
-        }
         return builder.append("]").toString();
     }
 
@@ -725,7 +714,6 @@
         parcel.writeParcelableArray(mFieldClassificationIds, flags);
         parcel.writeInt(mFlags);
         parcel.writeIntArray(mCancelIds);
-        parcel.writeParcelable(mInlineSuggestionSlices, flags);
         parcel.writeInt(mRequestId);
     }
 
@@ -781,16 +769,6 @@
             final int[] cancelIds = parcel.createIntArray();
             builder.setCancelTargetIds(cancelIds);
 
-            final ParceledListSlice<Slice> parceledInlineSuggestionSlices =
-                    parcel.readParcelable(null);
-            if (parceledInlineSuggestionSlices != null) {
-                final List<Slice> inlineSuggestionSlices = parceledInlineSuggestionSlices.getList();
-                final int size = inlineSuggestionSlices.size();
-                for (int i = 0; i < size; i++) {
-                    builder.addInlineSuggestionSlice(inlineSuggestionSlices.get(i));
-                }
-            }
-
             final FillResponse response = builder.build();
             response.setRequestId(parcel.readInt());
 
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/core/java/android/service/autofill/InlinePresentation.aidl
similarity index 88%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to core/java/android/service/autofill/InlinePresentation.aidl
index 1fefda4..eeddcc0 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/core/java/android/service/autofill/InlinePresentation.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.service.autofill;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+parcelable InlinePresentation;
diff --git a/core/java/android/service/autofill/InlinePresentation.java b/core/java/android/service/autofill/InlinePresentation.java
new file mode 100644
index 0000000..1568fb3
--- /dev/null
+++ b/core/java/android/service/autofill/InlinePresentation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.inline.InlinePresentationSpec;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Wrapper class holding a {@link Slice} and an {@link InlinePresentationSpec} for rendering UI
+ * for an Inline Suggestion.
+ */
+@DataClass(
+        genToString = true,
+        genHiddenConstDefs = true,
+        genEqualsHashCode = true)
+public final class InlinePresentation implements Parcelable {
+
+    private final @NonNull Slice mSlice;
+
+    private final @NonNull InlinePresentationSpec mInlinePresentationSpec;
+
+
+
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/autofill/InlinePresentation.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    @DataClass.Generated.Member
+    public InlinePresentation(
+            @NonNull Slice slice,
+            @NonNull InlinePresentationSpec inlinePresentationSpec) {
+        this.mSlice = slice;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSlice);
+        this.mInlinePresentationSpec = inlinePresentationSpec;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInlinePresentationSpec);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull Slice getSlice() {
+        return mSlice;
+    }
+
+    @DataClass.Generated.Member
+    public @NonNull InlinePresentationSpec getInlinePresentationSpec() {
+        return mInlinePresentationSpec;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "InlinePresentation { " +
+                "slice = " + mSlice + ", " +
+                "inlinePresentationSpec = " + mInlinePresentationSpec +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@android.annotation.Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(InlinePresentation other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        InlinePresentation that = (InlinePresentation) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mSlice, that.mSlice)
+                && java.util.Objects.equals(mInlinePresentationSpec, that.mInlinePresentationSpec);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mSlice);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mInlinePresentationSpec);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeTypedObject(mSlice, flags);
+        dest.writeTypedObject(mInlinePresentationSpec, flags);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ InlinePresentation(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        Slice slice = (Slice) in.readTypedObject(Slice.CREATOR);
+        InlinePresentationSpec inlinePresentationSpec = (InlinePresentationSpec) in.readTypedObject(InlinePresentationSpec.CREATOR);
+
+        this.mSlice = slice;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSlice);
+        this.mInlinePresentationSpec = inlinePresentationSpec;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInlinePresentationSpec);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<InlinePresentation> CREATOR
+            = new Parcelable.Creator<InlinePresentation>() {
+        @Override
+        public InlinePresentation[] newArray(int size) {
+            return new InlinePresentation[size];
+        }
+
+        @Override
+        public InlinePresentation createFromParcel(@NonNull Parcel in) {
+            return new InlinePresentation(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1578081082387L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/android/service/autofill/InlinePresentation.java",
+            inputSignatures = "private final @android.annotation.NonNull android.app.slice.Slice mSlice\nprivate final @android.annotation.NonNull android.view.inline.InlinePresentationSpec mInlinePresentationSpec\nclass InlinePresentation extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 484eddc..6334d9d 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -38,6 +38,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.service.autofill.Dataset;
 import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
 import android.util.Log;
 import android.util.Pair;
@@ -47,6 +48,7 @@
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAugmentedAutofillManagerClient;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.inputmethod.InlineSuggestionsRequest;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -106,10 +108,11 @@
         @Override
         public void onFillRequest(int sessionId, IBinder client, int taskId,
                 ComponentName componentName, AutofillId focusedId, AutofillValue focusedValue,
-                long requestTime, IFillCallback callback) {
+                long requestTime, @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+                IFillCallback callback) {
             mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnFillRequest,
                     AugmentedAutofillService.this, sessionId, client, taskId, componentName,
-                    focusedId, focusedValue, requestTime, callback));
+                    focusedId, focusedValue, requestTime, inlineSuggestionsRequest, callback));
         }
 
         @Override
@@ -212,6 +215,7 @@
     private void handleOnFillRequest(int sessionId, @NonNull IBinder client, int taskId,
             @NonNull ComponentName componentName, @NonNull AutofillId focusedId,
             @Nullable AutofillValue focusedValue, long requestTime,
+            @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
             @NonNull IFillCallback callback) {
         if (mAutofillProxies == null) {
             mAutofillProxies = new SparseArray<>();
@@ -236,9 +240,8 @@
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
-        // TODO(b/146453195): pass the inline suggestion request over.
-        onFillRequest(new FillRequest(proxy, /* inlineSuggestionsRequest= */null),
-                cancellationSignal, new FillController(proxy), new FillCallback(proxy));
+        onFillRequest(new FillRequest(proxy, inlineSuggestionsRequest), cancellationSignal,
+                new FillController(proxy), new FillCallback(proxy));
     }
 
     private void handleOnDestroyAllFillWindowsRequest() {
@@ -484,6 +487,14 @@
             }
         }
 
+        public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData) {
+            try {
+                mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}));
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
+            }
+        }
+
         // Used (mostly) for metrics.
         public void report(@ReportEvent int event) {
             if (sVerbose) Log.v(TAG, "report(): " + event);
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index 0251386..b767799 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -21,9 +21,12 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.service.autofill.Dataset;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.util.Log;
 
+import java.util.List;
+
 /**
  * Callback used to indicate at {@link FillRequest} has been fulfilled.
  *
@@ -45,7 +48,7 @@
      * Sets the response associated with the request.
      *
      * @param response response associated with the request, or {@code null} if the service
-     * could not provide autofill for the request.
+     *                 could not provide autofill for the request.
      */
     public void onSuccess(@Nullable FillResponse response) {
         if (sDebug) Log.d(TAG, "onSuccess(): " + response);
@@ -55,6 +58,12 @@
             return;
         }
 
+        List<Dataset> inlineSuggestions = response.getInlineSuggestions();
+        if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
+            mProxy.onInlineSuggestionsDataReady(inlineSuggestions);
+            return;
+        }
+
         final FillWindow fillWindow = response.getFillWindow();
         if (fillWindow != null) {
             fillWindow.show();
diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
index 103fc4d..4911348 100644
--- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
+++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl
@@ -22,6 +22,7 @@
 import android.service.autofill.augmented.IFillCallback;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
+import android.view.inputmethod.InlineSuggestionsRequest;
 
 import java.util.List;
 
@@ -35,7 +36,9 @@
     void onDisconnected();
     void onFillRequest(int sessionId, in IBinder autofillManagerClient, int taskId,
                        in ComponentName activityComponent, in AutofillId focusedId,
-                       in AutofillValue focusedValue, long requestTime, in IFillCallback callback);
+                       in AutofillValue focusedValue, long requestTime,
+                       in InlineSuggestionsRequest inlineSuggestionsRequest,
+                       in IFillCallback callback);
 
     void onDestroyAllFillWindowsRequest();
 }
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
index df18eb6..43a308c 100644
--- a/core/java/android/service/controls/Control.java
+++ b/core/java/android/service/controls/Control.java
@@ -16,6 +16,7 @@
 
 package android.service.controls;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
@@ -28,28 +29,26 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Represents a physical object that can be represented by a {@link ControlTemplate} and whose
  * properties may be modified through a {@link ControlAction}.
  *
- * The information is provided by a {@link ControlProviderService} and represents static
+ * The information is provided by a {@link ControlsProviderService} and represents static
  * information (not current status) about the device.
  * <p>
  * Each control needs a unique (per provider) identifier that is persistent across reboots of the
  * system.
  * <p>
  * Each {@link Control} will have a name, a subtitle and will optionally belong to a structure
- * and zone. Some of these values are defined by the user and/or the {@link ControlProviderService}
+ * and zone. Some of these values are defined by the user and/or the {@link ControlsProviderService}
  * and will be used to display the control as well as group them for management.
  * <p>
  * Each object will have an associated {@link DeviceTypes.DeviceType}. This will determine the icons and colors
  * used to display it.
  * <p>
- * The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in
- * non-interactive situations (for example when there's no state to display). This template is not
- * the one that will be shown with the current state and provide interactions. That template is set
- * using {@link ControlState}.
- * <p>
  * An {@link Intent} linking to the provider Activity that expands on this {@link Control} and
  * allows for further actions should be provided.
  * @hide
@@ -57,17 +56,52 @@
 public class Control implements Parcelable {
     private static final String TAG = "Control";
 
-    ;
+    private static final int NUM_STATUS = 5;
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATUS_UNKNOWN,
+            STATUS_OK,
+            STATUS_NOT_FOUND,
+            STATUS_ERROR,
+            STATUS_DISABLED,
+    })
+    public @interface Status {};
+
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * The device corresponding to the {@link Control} is responding correctly.
+     */
+    public static final int STATUS_OK = 1;
+
+    /**
+     * The device corresponding to the {@link Control} cannot be found or was removed.
+     */
+    public static final int STATUS_NOT_FOUND = 2;
+
+    /**
+     * The device corresponding to the {@link Control} is in an error state.
+     */
+    public static final int STATUS_ERROR = 3;
+
+    /**
+     * The {@link Control} is currently disabled.
+     */
+    public static final int STATUS_DISABLED = 4;
 
     private final @NonNull String mControlId;
-    private final @DeviceTypes.DeviceType
-    int mDeviceType;
+    private final @DeviceTypes.DeviceType int mDeviceType;
     private final @NonNull CharSequence mTitle;
     private final @NonNull CharSequence mSubtitle;
     private final @Nullable CharSequence mStructure;
     private final @Nullable CharSequence mZone;
     private final @NonNull PendingIntent mAppIntent;
-    private final @ControlTemplate.TemplateType int mPrimaryType;
+    private final @Status int mStatus;
+    private final @NonNull ControlTemplate mControlTemplate;
+    private final @NonNull CharSequence mStatusText;
 
     /**
      * @param controlId the unique persistent identifier for this object.
@@ -79,7 +113,6 @@
      * @param zone
      * @param appIntent a {@link PendingIntent} linking to a page to interact with the
      *                  corresponding device.
-     * @param primaryType the primary template for this type.
      */
     public Control(@NonNull String controlId,
             @DeviceTypes.DeviceType int deviceType,
@@ -88,11 +121,15 @@
             @Nullable CharSequence structure,
             @Nullable CharSequence zone,
             @NonNull PendingIntent appIntent,
-            int primaryType) {
+            @Status int status,
+            @NonNull ControlTemplate controlTemplate,
+            @NonNull CharSequence statusText) {
         Preconditions.checkNotNull(controlId);
         Preconditions.checkNotNull(title);
         Preconditions.checkNotNull(subtitle);
         Preconditions.checkNotNull(appIntent);
+        Preconditions.checkNotNull(controlTemplate);
+        Preconditions.checkNotNull(statusText);
         mControlId = controlId;
         if (!DeviceTypes.validDeviceType(deviceType)) {
             Log.e(TAG, "Invalid device type:" + deviceType);
@@ -105,7 +142,14 @@
         mStructure = structure;
         mZone = zone;
         mAppIntent = appIntent;
-        mPrimaryType = primaryType;
+        if (status < 0 || status >= NUM_STATUS) {
+            mStatus = STATUS_UNKNOWN;
+            Log.e(TAG, "Status unknown:" + status);
+        } else {
+            mStatus = status;
+        }
+        mControlTemplate = controlTemplate;
+        mStatusText = statusText;
     }
 
     public Control(Parcel in) {
@@ -124,7 +168,9 @@
             mZone = null;
         }
         mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
-        mPrimaryType = in.readInt();
+        mStatus = in.readInt();
+        mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
+        mStatusText = in.readCharSequence();
     }
 
     @NonNull
@@ -162,9 +208,19 @@
         return mAppIntent;
     }
 
-    @android.service.controls.templates.ControlTemplate.TemplateType
-    public int getPrimaryType() {
-        return mPrimaryType;
+    @Status
+    public int getStatus() {
+        return mStatus;
+    }
+
+    @NonNull
+    public ControlTemplate getControlTemplate() {
+        return mControlTemplate;
+    }
+
+    @NonNull
+    public CharSequence getStatusText() {
+        return mStatusText;
     }
 
     @Override
@@ -191,7 +247,9 @@
             dest.writeByte((byte) 0);
         }
         mAppIntent.writeToParcel(dest, flags);
-        dest.writeInt(mPrimaryType);
+        dest.writeInt(mStatus);
+        mControlTemplate.writeToParcel(dest, flags);
+        dest.writeCharSequence(mStatusText);
     }
 
     public static final Creator<Control> CREATOR = new Creator<Control>() {
@@ -209,32 +267,39 @@
     /**
      * Builder class for {@link Control}.
      *
-     * This class facilitates the creation of {@link Control}. It provides the following
-     * defaults for non-optional parameters:
+     * This class facilitates the creation of {@link Control} with no state.
+     * It provides the following defaults for non-optional parameters:
      * <ul>
      *     <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
      *     <li> Title: {@code ""}
      *     <li> Subtitle: {@code ""}
-     *     <li> Primary template: {@link ControlTemplate#TYPE_NONE}
+     * </ul>
+     * This fixes the values relating to state of the {@link Control} as required by
+     * {@link ControlsProviderService#onLoad}:
+     * <ul>
+     *     <li> Status: {@link Status#STATUS_UNKNOWN}
+     *     <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+     *     <li> Status text: {@code ""}
      * </ul>
      */
-    public static class Builder {
-        private static final String TAG = "Control.Builder";
-        private @NonNull String mControlId;
-        private @DeviceTypes.DeviceType
-        int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
-        private @NonNull CharSequence mTitle = "";
-        private @NonNull CharSequence mSubtitle = "";
-        private @Nullable CharSequence mStructure;
-        private @Nullable CharSequence mZone;
-        private @NonNull PendingIntent mAppIntent;
-        private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
+    public static class StatelessBuilder {
+        private static final String TAG = "StatelessBuilder";
+        protected @NonNull String mControlId;
+        protected @DeviceTypes.DeviceType int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
+        protected @NonNull CharSequence mTitle = "";
+        protected @NonNull CharSequence mSubtitle = "";
+        protected @Nullable CharSequence mStructure;
+        protected @Nullable CharSequence mZone;
+        protected @NonNull PendingIntent mAppIntent;
+        protected @Status int mStatus = STATUS_UNKNOWN;
+        protected @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
+        protected @NonNull CharSequence mStatusText = "";
 
         /**
          * @param controlId the identifier for the {@link Control}.
          * @param appIntent the pending intent linking to the device Activity.
          */
-        public Builder(@NonNull String controlId,
+        public StatelessBuilder(@NonNull String controlId,
                 @NonNull PendingIntent appIntent) {
             Preconditions.checkNotNull(controlId);
             Preconditions.checkNotNull(appIntent);
@@ -243,10 +308,10 @@
         }
 
         /**
-         * Creates a {@link Builder} using an existing {@link Control} as a base.
+         * Creates a {@link StatelessBuilder} using an existing {@link Control} as a base.
          * @param control base for the builder.
          */
-        public Builder(@NonNull Control control) {
+        public StatelessBuilder(@NonNull Control control) {
             Preconditions.checkNotNull(control);
             mControlId = control.mControlId;
             mDeviceType = control.mDeviceType;
@@ -255,7 +320,6 @@
             mStructure = control.mStructure;
             mZone = control.mZone;
             mAppIntent = control.mAppIntent;
-            mPrimaryType = control.mPrimaryType;
         }
 
         /**
@@ -263,14 +327,14 @@
          * @return {@code this}
          */
         @NonNull
-        public Builder setControlId(@NonNull String controlId) {
+        public StatelessBuilder setControlId(@NonNull String controlId) {
             Preconditions.checkNotNull(controlId);
             mControlId = controlId;
             return this;
         }
 
         @NonNull
-        public Builder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
+        public StatelessBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
             if (!DeviceTypes.validDeviceType(deviceType)) {
                 Log.e(TAG, "Invalid device type:" + deviceType);
                 mDeviceType = DeviceTypes.TYPE_UNKNOWN;
@@ -285,27 +349,27 @@
          * @return {@code this}
          */
         @NonNull
-        public Builder setTitle(@NonNull CharSequence title) {
+        public StatelessBuilder setTitle(@NonNull CharSequence title) {
             Preconditions.checkNotNull(title);
             mTitle = title;
             return this;
         }
 
         @NonNull
-        public Builder setSubtitle(@NonNull CharSequence subtitle) {
+        public StatelessBuilder setSubtitle(@NonNull CharSequence subtitle) {
             Preconditions.checkNotNull(subtitle);
             mSubtitle = subtitle;
             return this;
         }
 
         @NonNull
-        public Builder setStructure(@Nullable CharSequence structure) {
+        public StatelessBuilder setStructure(@Nullable CharSequence structure) {
             mStructure = structure;
             return this;
         }
 
         @NonNull
-        public Builder setZone(@Nullable CharSequence zone) {
+        public StatelessBuilder setZone(@Nullable CharSequence zone) {
             mZone = zone;
             return this;
         }
@@ -315,23 +379,13 @@
          * @return {@code this}
          */
         @NonNull
-        public Builder setAppIntent(@NonNull PendingIntent appIntent) {
+        public StatelessBuilder setAppIntent(@NonNull PendingIntent appIntent) {
             Preconditions.checkNotNull(appIntent);
             mAppIntent = appIntent;
             return this;
         }
 
         /**
-         * @param type type to use as default in the {@link Control}
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setPrimaryType(@ControlTemplate.TemplateType int type) {
-            mPrimaryType = type;
-            return this;
-        }
-
-        /**
          * Build a {@link Control}
          * @return a valid {@link Control}
          */
@@ -344,7 +398,108 @@
                     mStructure,
                     mZone,
                     mAppIntent,
-                    mPrimaryType);
+                    mStatus,
+                    mControlTemplate,
+                    mStatusText);
+        }
+    }
+
+    public static class StatefulBuilder extends StatelessBuilder {
+        private static final String TAG = "StatefulBuilder";
+
+        /**
+         * @param controlId the identifier for the {@link Control}.
+         * @param appIntent the pending intent linking to the device Activity.
+         */
+        public StatefulBuilder(@NonNull String controlId,
+                @NonNull PendingIntent appIntent) {
+            super(controlId, appIntent);
+        }
+
+        public StatefulBuilder(@NonNull Control control) {
+            super(control);
+            mStatus = control.mStatus;
+            mControlTemplate = control.mControlTemplate;
+            mStatusText = control.mStatusText;
+        }
+
+        /**
+         * @param controlId the identifier for the {@link Control}.
+         * @return {@code this}
+         */
+        @NonNull
+        public StatefulBuilder setControlId(@NonNull String controlId) {
+            super.setControlId(controlId);
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
+            super.setDeviceType(deviceType);
+            return this;
+        }
+
+        /**
+         * @param title the user facing name of the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public StatefulBuilder setTitle(@NonNull CharSequence title) {
+            super.setTitle(title);
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setSubtitle(@NonNull CharSequence subtitle) {
+            super.setSubtitle(subtitle);
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setStructure(@Nullable CharSequence structure) {
+            super.setStructure(structure);
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setZone(@Nullable CharSequence zone) {
+            super.setZone(zone);
+            return this;
+        }
+
+        /**
+         * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public StatefulBuilder setAppIntent(@NonNull PendingIntent appIntent) {
+            super.setAppIntent(appIntent);
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setStatus(@Status int status) {
+            if (status < 0 || status >= NUM_STATUS) {
+                mStatus = STATUS_UNKNOWN;
+                Log.e(TAG, "Status unknown:" + status);
+            } else {
+                mStatus = status;
+            }
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
+            Preconditions.checkNotNull(controlTemplate);
+            mControlTemplate = controlTemplate;
+            return this;
+        }
+
+        @NonNull
+        public StatefulBuilder setStatusText(@NonNull CharSequence statusText) {
+            Preconditions.checkNotNull(statusText);
+            mStatusText = statusText;
+            return this;
         }
     }
 }
diff --git a/core/java/android/service/controls/ControlState.aidl b/core/java/android/service/controls/ControlState.aidl
deleted file mode 100644
index 520d85b..0000000
--- a/core/java/android/service/controls/ControlState.aidl
+++ /dev/null
@@ -1,19 +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 android.service.controls;
-
-parcelable ControlState;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java
deleted file mode 100644
index 998fb54..0000000
--- a/core/java/android/service/controls/ControlState.java
+++ /dev/null
@@ -1,335 +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 android.service.controls;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.service.controls.templates.ControlTemplate;
-import android.util.Log;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Current state for a {@link Control}.
- *
- * Collects information to render the current state of a {@link Control} as well as possible action
- * that can be performed on it.
- * <p>
- * Additionally, this object is used to modify elements from the {@link Control} such as device
- * type, and intents. This information will last until it is again modified by a
- * {@link ControlState}.
- * @hide
- */
-public final class ControlState implements Parcelable {
-
-    private static final String TAG = "ControlState";
-
-    /**
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            STATUS_OK,
-            STATUS_NOT_FOUND,
-            STATUS_ERROR,
-            STATUS_DISABLED,
-    })
-    public @interface Status {};
-
-    /**
-     * The device corresponding to the {@link Control} is responding correctly.
-     */
-    public static final int STATUS_OK = 0;
-
-    /**
-     * The device corresponding to the {@link Control} cannot be found or was removed.
-     */
-    public static final int STATUS_NOT_FOUND = 1;
-
-    /**
-     * The device corresponding to the {@link Control} is in an error state.
-     */
-    public static final int STATUS_ERROR = 2;
-
-    /**
-     * The {@link Control} is currently disabled.
-     */
-    public static final int STATUS_DISABLED = 3;
-
-    private final @NonNull String mControlId;
-    private final @DeviceTypes.DeviceType int mDeviceType;
-    private final @Status int mStatus;
-    private final @NonNull ControlTemplate mControlTemplate;
-    private final @NonNull CharSequence mStatusText;
-    private final @Nullable PendingIntent mAppIntent;
-
-    /**
-     * @param controlId the identifier of the {@link Control} this object refers to.
-     * @param status the current status of the {@link Control}.
-     * @param deviceType the {@link DeviceTypes.DeviceType} to replace the one set in the
-     *                   {@link Control} or set in the last {@link ControlState}. In order to keep
-     *                   the current device type for this {@link Control}, the old value must be
-     *                   passed.
-     * @param controlTemplate the template to be used to render the {@link Control}. This can be
-     *                        of a different {@link ControlTemplate.TemplateType} than the
-     *                        one defined in {@link Control#getPrimaryType}
-     * @param statusText the user facing text describing the current status.
-     * @param appIntent the {@link PendingIntent} to replace the one set in the {@link Control} or
-     *                  set in the last {@link ControlState}. Pass {@code null} to use the last
-     *                  value set for this {@link Control}.
-     */
-    public ControlState(@NonNull String controlId,
-            @DeviceTypes.DeviceType int deviceType,
-            @Status int status,
-            @NonNull ControlTemplate controlTemplate,
-            @NonNull CharSequence statusText,
-            @Nullable PendingIntent appIntent) {
-        Preconditions.checkNotNull(controlId);
-        Preconditions.checkNotNull(controlTemplate);
-        Preconditions.checkNotNull(statusText);
-        mControlId = controlId;
-        if (!DeviceTypes.validDeviceType(deviceType)) {
-            Log.e(TAG, "Invalid device type:" + deviceType);
-            mDeviceType = DeviceTypes.TYPE_UNKNOWN;
-        } else {
-            mDeviceType = deviceType;
-        }
-        mStatus = status;
-        mControlTemplate = controlTemplate;
-        mStatusText = statusText;
-        mAppIntent = appIntent;
-    }
-
-    ControlState(Parcel in) {
-        mControlId = in.readString();
-        mDeviceType = in.readInt();
-        mStatus = in.readInt();
-        mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
-        mStatusText = in.readCharSequence();
-        if (in.readByte() == 1) {
-            mAppIntent = PendingIntent.CREATOR.createFromParcel(in);
-        } else {
-            mAppIntent = null;
-        }
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @NonNull
-    public String getControlId() {
-        return mControlId;
-    }
-
-    @DeviceTypes.DeviceType
-    public int getDeviceType() {
-        return mDeviceType;
-    }
-
-    @Nullable
-    public PendingIntent getAppIntent() {
-        return mAppIntent;
-    }
-
-    @Status
-    public int getStatus() {
-        return mStatus;
-    }
-
-    @NonNull
-    public ControlTemplate getControlTemplate() {
-        return mControlTemplate;
-    }
-
-    @NonNull
-    public CharSequence getStatusText() {
-        return mStatusText;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mControlId);
-        dest.writeInt(mDeviceType);
-        dest.writeInt(mStatus);
-        mControlTemplate.writeToParcel(dest, flags);
-        dest.writeCharSequence(mStatusText);
-        if (mAppIntent != null) {
-            dest.writeByte((byte) 1);
-            mAppIntent.writeToParcel(dest, flags);
-        } else {
-            dest.writeByte((byte) 0);
-        }
-    }
-
-    public static final Creator<ControlState> CREATOR = new Creator<ControlState>() {
-        @Override
-        public ControlState createFromParcel(Parcel source) {
-            return new ControlState(source);
-        }
-
-        @Override
-        public ControlState[] newArray(int size) {
-            return new ControlState[size];
-        }
-    };
-
-    /**
-     * Builder class for {@link ControlState}.
-     *
-     * This class facilitates the creation of {@link ControlState}. It provides the following
-     * defaults for non-optional parameters:
-     * <ul>
-     *     <li> Device type: {@link DeviceTypes#TYPE_UNKNOWN}
-     *     <li> Status: {@link ControlState#STATUS_OK}
-     *     <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
-     *     <li> Status text: {@code ""}
-     * </ul>
-     */
-    public static class Builder {
-        private static final String TAG = "ControlState.Builder";
-
-        private @NonNull String mControlId;
-        private @DeviceTypes.DeviceType
-        int mDeviceType = DeviceTypes.TYPE_UNKNOWN;
-        private @Status int mStatus = STATUS_OK;
-        private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
-        private @NonNull CharSequence mStatusText = "";
-        private @Nullable PendingIntent mAppIntent;
-
-        /**
-         * @param controlId the identifier of the {@link Control} that the resulting
-         *                  {@link ControlState} refers to.
-         */
-        public Builder(@NonNull String controlId) {
-            Preconditions.checkNotNull(controlId);
-            mControlId = controlId;
-        }
-
-        /**
-         * Creates a {@link Builder} using an existing {@link ControlState} as a base.
-         * @param controlState base for the builder.
-         */
-        public Builder(@NonNull ControlState controlState) {
-            Preconditions.checkNotNull(controlState);
-            mControlId = controlState.mControlId;
-            mDeviceType = controlState.mDeviceType;
-            mStatus = controlState.mStatus;
-            mControlTemplate = controlState.mControlTemplate;
-            mStatusText = controlState.mStatusText;
-            mAppIntent = controlState.mAppIntent;
-        }
-
-
-        /**
-         * @param controlId the identifier of the {@link Control} for the resulting object.
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setControlId(@NonNull String controlId) {
-            mControlId = controlId;
-            return this;
-        }
-
-        /**
-         * @param deviceType the device type of the {@link Control}.
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setDeviceType(@DeviceTypes.DeviceType int deviceType) {
-            if (!DeviceTypes.validDeviceType(deviceType)) {
-                Log.e(TAG, "Invalid device type:" + deviceType);
-                mDeviceType = DeviceTypes.TYPE_UNKNOWN;
-            } else {
-                mDeviceType = deviceType;
-            }
-            return this;
-        }
-
-        /**
-         * @param status the current status of the {@link Control}
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setStatus(@Status int status) {
-            mStatus = status;
-            return this;
-        }
-
-        /**
-         * @param controlTemplate the template to use when rendering the {@code Control}.
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
-            Preconditions.checkNotNull(controlTemplate);
-            mControlTemplate = controlTemplate;
-            return this;
-        }
-
-        /**
-         * @param statusText the user-visible description of the status.
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setStatusText(@NonNull CharSequence statusText) {
-            Preconditions.checkNotNull(statusText);
-            mStatusText = statusText;
-            return this;
-        }
-
-        /**
-         * @param appIntent the Pending Intent to replace the one defined in the corresponding
-         *                  {@link Control} or set by the last {@link ControlState}. Pass
-         *                  {@code null} to keep the last value.
-         * @return {@code this}
-         */
-        @NonNull
-        public Builder setAppIntent(@Nullable PendingIntent appIntent) {
-            mAppIntent = appIntent;
-            return this;
-        }
-
-        /**
-         * @return a new {@link ControlState}
-         */
-        public ControlState build() {
-            return new ControlState(mControlId, mDeviceType, mStatus, mControlTemplate, mStatusText,
-                    mAppIntent);
-        }
-
-        /**
-         * Creates a new {@link ControlState.Builder} for the given {@link Control}.
-         *
-         * This will set the corresponding identifier as well as the device type.
-         * @param control the {@link Control} to create a state for.
-         * @return a {@link ControlState.Builder} for a {@link Control}
-         */
-        public static Builder createForControl(Control control) {
-            return new Builder(control.getControlId()).setDeviceType(control.getDeviceType());
-        }
-    }
-}
-
diff --git a/core/java/android/service/controls/ControlsProviderService.java b/core/java/android/service/controls/ControlsProviderService.java
index 8a95f4d..eca8541 100644
--- a/core/java/android/service/controls/ControlsProviderService.java
+++ b/core/java/android/service/controls/ControlsProviderService.java
@@ -25,8 +25,15 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.RemoteException;
 import android.service.controls.actions.ControlAction;
+import android.service.controls.templates.ControlTemplate;
+import android.text.TextUtils;
+import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -38,8 +45,14 @@
 
     @SdkConstant(SdkConstantType.SERVICE_ACTION)
     public static final String CONTROLS_ACTION = "android.service.controls.ControlsProviderService";
+    public static final String CALLBACK_BUNDLE = "CALLBACK_BUNDLE";
+    public static final String CALLBACK_BINDER = "CALLBACK_BINDER";
+    public static final String CALLBACK_TOKEN = "CALLBACK_TOKEN";
+
+    public final String TAG = getClass().getSimpleName();
 
     private IControlsProviderCallback mCallback;
+    private IBinder mToken;
     private RequestHandler mHandler;
 
     /**
@@ -67,16 +80,80 @@
      */
     public abstract void onAction(@NonNull String controlId, @NonNull ControlAction action);
 
-    protected IControlsProviderCallback getControlsProviderCallback() {
-        return mCallback;
+    /**
+     * Sends a list of the controls available from this service.
+     *
+     * The items in the list must not have state information (as created by
+     * {@link Control.StatelessBuilder}).
+     * @param controls
+     */
+    public final void onLoad(@NonNull List<Control> controls) {
+        Preconditions.checkNotNull(controls);
+        List<Control> list = new ArrayList<>();
+        for (Control control: controls) {
+            if (control == null) {
+                Log.e(TAG, "onLoad: null control.");
+            }
+            if (isStateless(control)) {
+                list.add(control);
+            } else {
+                Log.w(TAG, "onLoad: control is not stateless.");
+                list.add(new Control.StatelessBuilder(control).build());
+            }
+        }
+        try {
+            mCallback.onLoad(mToken, list);
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sends a list of the controls requested by {@link ControlsProviderService#subscribe} with
+     * their state.
+     * @param statefulControls
+     */
+    public final void onRefreshState(@NonNull List<Control> statefulControls) {
+        Preconditions.checkNotNull(statefulControls);
+        try {
+            mCallback.onRefreshState(mToken, statefulControls);
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Sends the response of a command in the specified {@link Control}.
+     * @param controlId
+     * @param response
+     */
+    public final void onControlActionResponse(
+            @NonNull String controlId, @ControlAction.ResponseResult int response) {
+        Preconditions.checkNotNull(controlId);
+        if (!ControlAction.isValidResponse(response)) {
+            Log.e(TAG, "Not valid response result: " + response);
+            response = ControlAction.RESPONSE_UNKNOWN;
+        }
+        try {
+            mCallback.onControlActionResponse(mToken, controlId, response);
+        } catch (RemoteException ex) {
+            ex.rethrowAsRuntimeException();
+        }
+    }
+
+    private boolean isStateless(Control control) {
+        return (control.getStatus() == Control.STATUS_UNKNOWN
+                    && control.getControlTemplate().getTemplateType() == ControlTemplate.TYPE_NONE
+                    && TextUtils.isEmpty(control.getStatusText()));
     }
 
     @Override
     public IBinder onBind(Intent intent) {
         mHandler = new RequestHandler(Looper.getMainLooper());
 
-        Bundle bundle = intent.getBundleExtra("CALLBACK_BUNDLE");
-        IBinder callbackBinder = bundle.getBinder("CALLBACK_BINDER");
+        Bundle bundle = intent.getBundleExtra(CALLBACK_BUNDLE);
+        IBinder callbackBinder = bundle.getBinder(CALLBACK_BINDER);
+        mToken = bundle.getBinder(CALLBACK_TOKEN);
         mCallback = IControlsProviderCallback.Stub.asInterface(callbackBinder);
 
         return new IControlsProvider.Stub() {
@@ -94,7 +171,7 @@
 
             public void onAction(String id, ControlAction action) {
                 ActionMessage msg = new ActionMessage(id, action);
-                mHandler.obtainMessage(RequestHandler.MSG_SUBSCRIBE, msg).sendToTarget();
+                mHandler.obtainMessage(RequestHandler.MSG_ON_ACTION, msg).sendToTarget();
             }
         };
     }
diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsProviderCallback.aidl
index 3dbb68c..91f6a79 100644
--- a/core/java/android/service/controls/IControlsProviderCallback.aidl
+++ b/core/java/android/service/controls/IControlsProviderCallback.aidl
@@ -17,13 +17,12 @@
 package android.service.controls;
 
 import android.service.controls.Control;
-import android.service.controls.ControlState;
 
 /** @hide */
 oneway interface IControlsProviderCallback {
-    void onLoad(in List<Control> controls);
+    void onLoad(in IBinder token, in List<Control> controls);
 
-    void onRefreshState(in List<ControlState> controlStates);
+    void onRefreshState(in IBinder token, in List<Control> statefulControls);
 
-    void onControlActionResponse(in String controlId, int response);
+    void onControlActionResponse(in IBinder token, in String controlId, int response);
 }
\ No newline at end of file
diff --git a/core/java/android/service/controls/actions/ControlAction.java b/core/java/android/service/controls/actions/ControlAction.java
index 63ae9bd..83d1cf8 100644
--- a/core/java/android/service/controls/actions/ControlAction.java
+++ b/core/java/android/service/controls/actions/ControlAction.java
@@ -23,8 +23,8 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.service.controls.templates.ControlTemplate;
 import android.service.controls.IControlsProviderCallback;
+import android.service.controls.templates.ControlTemplate;
 
 import com.android.internal.util.Preconditions;
 
@@ -82,11 +82,17 @@
 
     public static final @ActionType int TYPE_COMMAND = 5;
 
+
+    public static final boolean isValidResponse(@ResponseResult int response) {
+        return (response >= 0 && response < NUM_RESPONSE_TYPES);
+    }
+    private static final int NUM_RESPONSE_TYPES = 6;
     /**
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({
+            RESPONSE_UNKNOWN,
             RESPONSE_OK,
             RESPONSE_FAIL,
             RESPONSE_CHALLENGE_ACK,
@@ -95,31 +101,33 @@
     })
     public @interface ResponseResult {};
 
+    public static final @ResponseResult int RESPONSE_UNKNOWN = 0;
+
     /**
      * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
      * the action has been performed. The action may still fail later and the state may not change.
      */
-    public static final @ResponseResult int RESPONSE_OK = 0;
+    public static final @ResponseResult int RESPONSE_OK = 1;
     /**
      * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
      * the action has failed.
      */
-    public static final @ResponseResult int RESPONSE_FAIL = 1;
+    public static final @ResponseResult int RESPONSE_FAIL = 2;
     /**
      * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
      * in order for the action to be performed, acknowledgment from the user is required.
      */
-    public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2;
+    public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 3;
     /**
      * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
      * in order for the action to be performed, a PIN is required.
      */
-    public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3;
+    public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 4;
     /**
      * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
      * in order for the action to be performed, an alphanumeric passphrase is required.
      */
-    public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4;
+    public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 5;
 
     /**
      * The {@link ActionType} associated with this class.
diff --git a/core/java/android/service/controls/templates/ControlTemplate.java b/core/java/android/service/controls/templates/ControlTemplate.java
index f39b26e..bf194f8 100644
--- a/core/java/android/service/controls/templates/ControlTemplate.java
+++ b/core/java/android/service/controls/templates/ControlTemplate.java
@@ -23,7 +23,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.service.controls.Control;
-import android.service.controls.ControlState;
 import android.service.controls.actions.ControlAction;
 
 import com.android.internal.util.Preconditions;
@@ -34,12 +33,11 @@
 /**
  * An abstract input template for a {@link Control}.
  *
- * Specifies what layout is presented to the user when a {@link ControlState} is assigned to a
- * particular {@link Control}.
+ * Specifies what layout is presented to the user for a given {@link Control}.
  * <p>
  * Some instances of {@link Control} can originate actions (via user interaction) to modify its
- * associated state. The actions available to a given {@link Control} in a particular
- * {@link ControlState} are determined by its {@link ControlTemplate}.
+ * associated state. The actions available to a given {@link Control} are determined by its
+ * {@link ControlTemplate}.
  * @see ControlAction
  * @hide
  */
diff --git a/core/java/android/service/controls/templates/RangeTemplate.java b/core/java/android/service/controls/templates/RangeTemplate.java
index 5624f88..bb79d83 100644
--- a/core/java/android/service/controls/templates/RangeTemplate.java
+++ b/core/java/android/service/controls/templates/RangeTemplate.java
@@ -21,7 +21,6 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.service.controls.Control;
-import android.service.controls.ControlState;
 import android.service.controls.actions.FloatAction;
 
 /**
@@ -61,7 +60,7 @@
      * @param templateId the identifier for this template object
      * @param minValue minimum value for the input
      * @param maxValue maximum value for the input
-     * @param currentValue the current value of the {@link ControlState} containing this object.
+     * @param currentValue the current value of the {@link Control} containing this object.
      * @param stepValue minimum value of increments/decrements when interacting with this control.
      * @param formatString a formatting string as per {@link String#format} used to display the
      *                    {@code currentValue}. If {@code null} is passed, the "%.1f" is used.
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/core/java/android/service/controls/templates/TemperatureControlTemplate.aidl
similarity index 94%
rename from core/java/android/service/controls/templates/ThermostatTemplate.aidl
rename to core/java/android/service/controls/templates/TemperatureControlTemplate.aidl
index 1fefda4..7994d26 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/core/java/android/service/controls/templates/TemperatureControlTemplate.aidl
@@ -16,4 +16,4 @@
 
 package android.service.controls.templates;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+parcelable TemperatureControlTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 38de794..de4a551 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -21,9 +21,9 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
 import android.app.AlarmManager;
 import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.ColorDrawable;
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index c04ac59..80d054b 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,7 +22,6 @@
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -33,6 +32,7 @@
 import android.app.Person;
 import android.app.Service;
 import android.companion.CompanionDeviceManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index b8378a3..389040c 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -17,11 +17,10 @@
 package android.service.notification;
 
 import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.Person;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index dedc3b7..3f9462c 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -20,11 +20,11 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.NotificationManager;
 import android.app.NotificationManager.Policy;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index cf56eae..67925bf 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 0de17ca..36e057f 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -18,8 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/service/vr/VrListenerService.java b/core/java/android/service/vr/VrListenerService.java
index 3c38495..2758ace 100644
--- a/core/java/android/service/vr/VrListenerService.java
+++ b/core/java/android/service/vr/VrListenerService.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
index aa6123f..ffbdb03 100644
--- a/core/java/android/service/wallpaper/Android.bp
+++ b/core/java/android/service/wallpaper/Android.bp
@@ -6,6 +6,8 @@
         "I*.aidl",
     ],
 
+    libs: ["unsupportedappusage"],
+
     // Enforce that the library is built against java 8 so that there are
     // no compatibility issues with launcher
     java_version: "1.8",
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index e50d6c2..9a76a1b 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -20,11 +20,11 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
 import android.app.WallpaperColors;
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 172495f..c861fa7 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -21,7 +21,7 @@
 import android.annotation.RawRes;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 2fab404..a8aea7c 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -18,7 +18,7 @@
 import static android.provider.Settings.Secure.getString;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 7841f99..e08a06a 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -21,7 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
@@ -369,6 +369,21 @@
     @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION)
     public static final int LISTEN_OUTGOING_EMERGENCY_SMS                   = 0x20000000;
 
+    /**
+     * Listen for Registration Failures.
+     *
+     * Listen for indications that a registration procedure has failed in either the CS or PS
+     * 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}).
+     *
+     * @see #onRegistrationFailed()
+     */
+    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
+    public static final int LISTEN_REGISTRATION_FAILURE = 0x40000000;
+
     /*
      * Subscription used to listen to the phone state changes
      * @hide
@@ -932,6 +947,38 @@
     }
 
     /**
+     * Report that Registration or a Location/Routing/Tracking Area update has failed.
+     *
+     * <p>Indicate whenever a registration procedure, including a location, routing, or tracking
+     * area update fails. This includes procedures that do not necessarily result in a change of
+     * the modem's registration status. If the modem's registration status changes, that is
+     * reflected in the onNetworkStateChanged() and subsequent get{Voice/Data}RegistrationState().
+     *
+     * <p>Because registration failures are ephemeral, this callback is not sticky.
+     * Registrants will not receive the most recent past value when registering.
+     *
+     * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+     *        for the cell (for example, all components of the CGI or ECGI).
+     * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+     *         cell that was chosen for the failed registration attempt.
+     * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+     * @param causeCode the primary failure cause code of the procedure.
+     *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+     *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+     *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+     *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+     *        Integer.MAX_VALUE if this value is unused.
+     * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+     *        For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+     *        included as an additionalCauseCode. For LTE (ESM), cause codes are in
+     *        TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+     */
+    public void onRegistrationFailed(@NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+            @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+        // default implementation empty
+    }
+
+    /**
      * The callback methods need to be called on the handler thread where
      * this object was created.  If the binder did that for us it'd be nice.
      *
@@ -1203,6 +1250,18 @@
                             () -> psl.onImsCallDisconnectCauseChanged(disconnectCause)));
 
         }
+
+        public void onRegistrationFailed(@NonNull CellIdentity cellIdentity,
+                @NonNull String chosenPlmn, @NetworkRegistrationInfo.Domain int domain,
+                int causeCode, int additionalCauseCode) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(() -> psl.onRegistrationFailed(
+                            cellIdentity, chosenPlmn, domain, causeCode, additionalCauseCode)));
+            // default implementation empty
+        }
     }
 
 
diff --git a/core/java/android/telephony/Rlog.java b/core/java/android/telephony/Rlog.java
index cdab2dc..2afdd33 100644
--- a/core/java/android/telephony/Rlog.java
+++ b/core/java/android/telephony/Rlog.java
@@ -16,12 +16,11 @@
 
 package android.telephony;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.text.TextUtils;
-import android.util.Log;
-
-import android.annotation.UnsupportedAppUsage;
 import android.util.Base64;
+import android.util.Log;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
diff --git a/core/java/android/telephony/SubscriptionPlan.java b/core/java/android/telephony/SubscriptionPlan.java
index 28a5c20..ff2f4ad 100644
--- a/core/java/android/telephony/SubscriptionPlan.java
+++ b/core/java/android/telephony/SubscriptionPlan.java
@@ -228,24 +228,6 @@
     }
 
     /**
-     * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask}
-     * @hide
-     */
-    public long getNetworkTypesBitMask() {
-        // calculate bitmask the first time and save for future calls
-        if (networkTypesBitMask == 0) {
-            if (networkTypes == null) {
-                networkTypesBitMask = ~0;
-            } else {
-                for (int networkType : networkTypes) {
-                    networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
-                }
-            }
-        }
-        return networkTypesBitMask;
-    }
-
-    /**
      * Return an iterator that will return all valid data usage cycles based on
      * any recurrence rules. The iterator starts from the currently active cycle
      * and walks backwards through time.
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 1b2feda..9387a2c 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.CallState;
 import android.telephony.Annotation.DataActivityType;
 import android.telephony.Annotation.DataFailureCause;
@@ -352,7 +353,7 @@
      * @param subId for which data connection state changed.
      * @param slotIndex for which data connections state changed. Can be derived from subId except
      * when subId is invalid.
-     * @param apnType the APN type that triggered this update
+     * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
      * @param preciseState the PreciseDataConnectionState
      *
      * @see android.telephony.PreciseDataConnection
@@ -360,7 +361,7 @@
      * @hide
      */
     public void notifyDataConnectionForSubscriber(int slotIndex, int subId,
-            String apnType, PreciseDataConnectionState preciseState) {
+            @ApnType int apnType, PreciseDataConnectionState preciseState) {
         try {
             sRegistry.notifyDataConnectionForSubscriber(
                     slotIndex, subId, apnType, preciseState);
@@ -553,13 +554,13 @@
      * @param subId for which data connection failed.
      * @param slotIndex for which data conenction failed. Can be derived from subId except when
      * subId is invalid.
-     * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN.
+     * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
      * @param apn the APN {@link ApnSetting#getApnName()} of this data connection.
      * @param failCause data fail cause.
      *
      * @hide
      */
-    public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, String apnType,
+    public void notifyPreciseDataConnectionFailed(int subId, int slotIndex, @ApnType int apnType,
         String apn, @DataFailureCause int failCause) {
         try {
             sRegistry.notifyPreciseDataConnectionFailed(slotIndex, subId, apnType, apn, failCause);
@@ -614,9 +615,9 @@
      * Notify call disconnect causes which contains {@link DisconnectCause} and {@link
      * android.telephony.PreciseDisconnectCause}.
      *
-     * @param subId for which call disconnected.
      * @param slotIndex for which call disconnected. Can be derived from subId except when subId is
      * invalid.
+     * @param subId for which call disconnected.
      * @param cause {@link DisconnectCause} for the disconnected call.
      * @param preciseCause {@link android.telephony.PreciseDisconnectCause} for the disconnected
      * call.
@@ -675,4 +676,36 @@
 
         }
     }
+
+    /**
+     * Report that Registration or a Location/Routing/Tracking Area update has failed.
+     *
+     * @param slotIndex for which call disconnected. Can be derived from subId except when subId is
+     * invalid.
+     * @param subId for which cellinfo changed.
+     * @param cellIdentity the CellIdentity, which must include the globally unique identifier
+     *        for the cell (for example, all components of the CGI or ECGI).
+     * @param chosenPlmn a 5 or 6 digit alphanumeric PLMN (MCC|MNC) among those broadcast by the
+     *         cell that was chosen for the failed registration attempt.
+     * @param domain DOMAIN_CS, DOMAIN_PS or both in case of a combined procedure.
+     * @param causeCode the primary failure cause code of the procedure.
+     *        For GSM/UMTS (MM), values are in TS 24.008 Sec 10.5.95
+     *        For GSM/UMTS (GMM), values are in TS 24.008 Sec 10.5.147
+     *        For LTE (EMM), cause codes are TS 24.301 Sec 9.9.3.9
+     *        For NR (5GMM), cause codes are TS 24.501 Sec 9.11.3.2
+     *        Integer.MAX_VALUE if this value is unused.
+     * @param additionalCauseCode the cause code of any secondary/combined procedure if appropriate.
+     *        For UMTS, if a combined attach succeeds for PS only, then the GMM cause code shall be
+     *        included as an additionalCauseCode. For LTE (ESM), cause codes are in
+     *        TS 24.301 9.9.4.4. Integer.MAX_VALUE if this value is unused.
+     */
+    public void notifyRegistrationFailed(int slotIndex, int subId,
+            @NonNull CellIdentity cellIdentity, @NonNull String chosenPlmn,
+            @NetworkRegistrationInfo.Domain int domain, int causeCode, int additionalCauseCode) {
+        try {
+            sRegistry.notifyRegistrationFailed(slotIndex, subId, cellIdentity,
+                    chosenPlmn, domain, causeCode, additionalCauseCode);
+        } catch (RemoteException ex) {
+        }
+    }
 }
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index bb7fb44..f1e2181 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -16,7 +16,7 @@
 
 package android.text;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.icu.lang.UCharacter;
 import android.icu.lang.UCharacterDirection;
 import android.icu.lang.UProperty;
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index cf6987c..3ee1a90 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -16,7 +16,7 @@
 
 package android.text;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 32982f9..c60d446 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -20,7 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Build;
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 19c55d5..b5688a4 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -21,7 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.net.Uri;
 
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 18f8db2..55af087 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -16,9 +16,9 @@
 
 package android.text;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Typeface;
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index a9a7b2f..96e7bd0 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -17,7 +17,7 @@
 package android.text;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.Preconditions;
 
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2c2c295..8a4497a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -18,7 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 68199a4..57c1d23 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -17,7 +17,7 @@
 package android.text;
 
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.text.BreakIterator;
 
diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java
index 362825a..81bdd65 100644
--- a/core/java/android/text/SpanSet.java
+++ b/core/java/android/text/SpanSet.java
@@ -16,7 +16,8 @@
 
 package android.text;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.lang.reflect.Array;
 import java.util.Arrays;
 
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 0d29da0..ac27e3d 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -17,7 +17,7 @@
 package android.text;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.BaseCanvas;
 import android.graphics.Paint;
 import android.util.Log;
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index a986633..b85ef76 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -16,7 +16,7 @@
 
 package android.text;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 69cfc03..85e2d98 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -20,7 +20,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Paint;
 import android.graphics.text.LineBreaker;
 import android.os.Build;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 1c50d73..3c51fa7 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -19,7 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetricsInt;
diff --git a/core/java/android/text/TextPaint.java b/core/java/android/text/TextPaint.java
index d5aad33..73825b1 100644
--- a/core/java/android/text/TextPaint.java
+++ b/core/java/android/text/TextPaint.java
@@ -18,7 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.Px;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Paint;
 
 /**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 5bda867..df4ead1 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -24,7 +24,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.PluralsRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.icu.lang.UCharacter;
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 3c8de94..0863a81 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -17,9 +17,8 @@
 package android.text.format;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 0c27923..ce676e0 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -16,7 +16,7 @@
 
 package android.text.format;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index d7baa10..17d3ae4 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.icu.text.MeasureFormat;
diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java
index 5a7c98d..305b056 100644
--- a/core/java/android/text/method/AllCapsTransformationMethod.java
+++ b/core/java/android/text/method/AllCapsTransformationMethod.java
@@ -17,7 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Rect;
 import android.text.Spanned;
diff --git a/core/java/android/text/method/HideReturnsTransformationMethod.java b/core/java/android/text/method/HideReturnsTransformationMethod.java
index 440a4b1..40ce871 100644
--- a/core/java/android/text/method/HideReturnsTransformationMethod.java
+++ b/core/java/android/text/method/HideReturnsTransformationMethod.java
@@ -16,7 +16,7 @@
 
 package android.text.method;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 /**
diff --git a/core/java/android/text/method/LinkMovementMethod.java b/core/java/android/text/method/LinkMovementMethod.java
index a0c44a8..c544c41 100644
--- a/core/java/android/text/method/LinkMovementMethod.java
+++ b/core/java/android/text/method/LinkMovementMethod.java
@@ -16,7 +16,7 @@
 
 package android.text.method;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.text.Layout;
 import android.text.NoCopySpan;
diff --git a/core/java/android/text/method/MetaKeyKeyListener.java b/core/java/android/text/method/MetaKeyKeyListener.java
index ec7ed34b..d1d7c96 100644
--- a/core/java/android/text/method/MetaKeyKeyListener.java
+++ b/core/java/android/text/method/MetaKeyKeyListener.java
@@ -16,7 +16,7 @@
 
 package android.text.method;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.text.Editable;
 import android.text.NoCopySpan;
 import android.text.Spannable;
diff --git a/core/java/android/text/method/PasswordTransformationMethod.java b/core/java/android/text/method/PasswordTransformationMethod.java
index c96fc5d..53553be 100644
--- a/core/java/android/text/method/PasswordTransformationMethod.java
+++ b/core/java/android/text/method/PasswordTransformationMethod.java
@@ -16,7 +16,7 @@
 
 package android.text.method;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Handler;
diff --git a/core/java/android/text/method/TransformationMethod2.java b/core/java/android/text/method/TransformationMethod2.java
index 0bf401a..8d5ec24 100644
--- a/core/java/android/text/method/TransformationMethod2.java
+++ b/core/java/android/text/method/TransformationMethod2.java
@@ -15,7 +15,7 @@
  */
 package android.text.method;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * TransformationMethod2 extends the TransformationMethod interface
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index 313567a..d766186 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -17,7 +17,7 @@
 package android.text.method;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.icu.lang.UCharacter;
 import android.icu.lang.UProperty;
 import android.icu.text.BreakIterator;
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 9b1dfbf..ad61788 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Px;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.os.Build;
diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java
index 1a508a1..f37e423 100644
--- a/core/java/android/text/style/DynamicDrawableSpan.java
+++ b/core/java/android/text/style/DynamicDrawableSpan.java
@@ -22,7 +22,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index bfb2873..b23c2b7 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -17,8 +17,8 @@
 package android.text.style;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index 98f58be..ec55aeb 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -19,7 +19,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index 6ffde38..e8ec3c6 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -16,7 +16,7 @@
 
 package android.text.style;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index d958dde..2b04a7a 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -16,7 +16,7 @@
 
 package android.text.style;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextPaint;
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index c000ae3..be01bfb 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -19,7 +19,7 @@
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Color;
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 993bbe8..2aca36a 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java
index de182da..59a05ac 100644
--- a/core/java/android/transition/ChangeBounds.java
+++ b/core/java/android/transition/ChangeBounds.java
@@ -22,7 +22,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.RectEvaluator;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
diff --git a/core/java/android/transition/Scene.java b/core/java/android/transition/Scene.java
index 8d4db54..52a97e3 100644
--- a/core/java/android/transition/Scene.java
+++ b/core/java/android/transition/Scene.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Build;
 import android.util.SparseArray;
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 0feab4d..e511584 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -20,7 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Path;
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 0e5252e..1b0612e 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -17,7 +17,7 @@
 package android.transition;
 
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.ArrayMap;
 import android.util.Log;
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 3f44e48..4cf0a36 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
 
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 4dda709..7f652ba 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -18,7 +18,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import libcore.util.EmptyArray;
 
diff --git a/core/java/android/util/Base64.java b/core/java/android/util/Base64.java
index ecc0c9c..92abd7c 100644
--- a/core/java/android/util/Base64.java
+++ b/core/java/android/util/Base64.java
@@ -16,7 +16,8 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.UnsupportedEncodingException;
 
 /**
diff --git a/core/java/android/util/Base64OutputStream.java b/core/java/android/util/Base64OutputStream.java
index 230a3a5..48fadeb 100644
--- a/core/java/android/util/Base64OutputStream.java
+++ b/core/java/android/util/Base64OutputStream.java
@@ -16,7 +16,8 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index bc5edf8..6d5e830 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 import java.io.PrintWriter;
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 451a669..9f6065e 100755
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.SystemProperties;
 
 
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 65d825a..c9dc05b 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -17,7 +17,7 @@
 package android.util;
 
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.BufferedReader;
 import java.io.FileReader;
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
index d86ebf3..721e6b3 100644
--- a/core/java/android/util/IconDrawableFactory.java
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -15,8 +15,8 @@
  */
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.UserIdInt;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageItemInfo;
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 6a6bccf..fda5e0d 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.SystemClock;
 
 import java.io.FileDescriptor;
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 50779031..3f679bba 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.DeadSystemException;
 
 import com.android.internal.os.RuntimeInit;
diff --git a/core/java/android/util/LogWriter.java b/core/java/android/util/LogWriter.java
index b062ace..a674ae1 100644
--- a/core/java/android/util/LogWriter.java
+++ b/core/java/android/util/LogWriter.java
@@ -16,7 +16,8 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.Writer;
 
 /** @hide */
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 6f4aa52..93bcd6b 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -17,7 +17,7 @@
 package android.util;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index a0edd04..c05dd9d 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 
 import com.android.internal.util.ArrayUtils;
diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java
index 3cbf727d..3f7fdd8 100644
--- a/core/java/android/util/LruCache.java
+++ b/core/java/android/util/LruCache.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.LinkedHashMap;
 import java.util.Map;
diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 0eeef70..971e161 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Rect;
 
 /**
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index da566c9..2223d14 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Resources;
@@ -175,4 +175,21 @@
     public long getCachedNtpTimeReference() {
         return mCachedNtpElapsedRealtime;
     }
+
+    /**
+     * Returns the combination of {@link #getCachedNtpTime()} and {@link
+     * #getCachedNtpTimeReference()} as a {@link TimestampedValue}. This method is useful when
+     * passing the time to another component that will adjust for elapsed time.
+     *
+     * @throws IllegalStateException if there is no cached value
+     */
+    public TimestampedValue<Long> getCachedNtpTimeSignal() {
+        if (!mHasCache) {
+            throw new IllegalStateException("Missing authoritative time source");
+        }
+        if (LOGD) Log.d(TAG, "getCachedNtpTimeSignal() cache hit");
+
+        return new TimestampedValue<>(mCachedNtpElapsedRealtime, mCachedNtpTime);
+    }
+
 }
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 5342d5d..1e5ec0b 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -14,7 +14,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Path;
 
 import dalvik.annotation.optimization.FastNative;
diff --git a/core/java/android/util/Pools.java b/core/java/android/util/Pools.java
index e242fe5..7ae32441 100644
--- a/core/java/android/util/Pools.java
+++ b/core/java/android/util/Pools.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Helper class for crating pools of objects. An example use looks like this:
diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java
index 39e8b14..5930000 100644
--- a/core/java/android/util/Rational.java
+++ b/core/java/android/util/Rational.java
@@ -15,9 +15,10 @@
  */
 package android.util;
 
-import static com.android.internal.util.Preconditions.*;
+import static com.android.internal.util.Preconditions.checkNotNull;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.IOException;
 import java.io.InvalidObjectException;
 
diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java
index 9c60228..a570e5e 100644
--- a/core/java/android/util/RecurrenceRule.java
+++ b/core/java/android/util/RecurrenceRule.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/android/util/Singleton.java b/core/java/android/util/Singleton.java
index 15c6b5b..92646b4 100644
--- a/core/java/android/util/Singleton.java
+++ b/core/java/android/util/Singleton.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Singleton helper class for lazily initialization.
diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java
index a85120f..2c8bbbf 100644
--- a/core/java/android/util/Slog.java
+++ b/core/java/android/util/Slog.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 /**
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index d3367c1..dae760f 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index d6e0e53..846df39 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 1ca1717..d4f6685 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 74cff20..8439f5a 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.SystemClock;
 
diff --git a/core/java/android/util/TrustedTime.java b/core/java/android/util/TrustedTime.java
index c78665d..1360f87 100644
--- a/core/java/android/util/TrustedTime.java
+++ b/core/java/android/util/TrustedTime.java
@@ -16,7 +16,7 @@
 
 package android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Interface that provides trusted time information, possibly coming from an NTP
diff --git a/core/java/android/util/XmlPullAttributes.java b/core/java/android/util/XmlPullAttributes.java
index 32fe16f..d83b355 100644
--- a/core/java/android/util/XmlPullAttributes.java
+++ b/core/java/android/util/XmlPullAttributes.java
@@ -16,13 +16,12 @@
 
 package android.util;
 
-import org.xmlpull.v1.XmlPullParser;
-
-import android.annotation.UnsupportedAppUsage;
-import android.util.AttributeSet;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.XmlUtils;
 
+import org.xmlpull.v1.XmlPullParser;
+
 /**
  * Provides an implementation of AttributeSet on top of an XmlPullParser.
  */
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index 5f9bf39..bee04f4 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Configuration;
 
 import java.text.BreakIterator;
diff --git a/core/java/android/view/ActionMode.java b/core/java/android/view/ActionMode.java
index 6b200e1..d9f9d03 100644
--- a/core/java/android/view/ActionMode.java
+++ b/core/java/android/view/ActionMode.java
@@ -19,10 +19,9 @@
 
 import android.annotation.StringRes;
 import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Rect;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 /**
  * Represents a contextual mode of the user interface. Action modes can be used to provide
  * alternative interaction modes and replace parts of the normal UI until finished.
diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java
index cd7e67e..e1be0fe 100644
--- a/core/java/android/view/ActionProvider.java
+++ b/core/java/android/view/ActionProvider.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.Log;
 
diff --git a/core/java/android/view/AppTransitionAnimationSpec.java b/core/java/android/view/AppTransitionAnimationSpec.java
index 4c0ed52..330061f 100644
--- a/core/java/android/view/AppTransitionAnimationSpec.java
+++ b/core/java/android/view/AppTransitionAnimationSpec.java
@@ -1,6 +1,6 @@
 package android.view;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Parcel;
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
index 61ccac9..95b2c70 100644
--- a/core/java/android/view/BatchedInputEventReceiver.java
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -16,7 +16,7 @@
 
 package android.view;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Looper;
 
 /**
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 08e4eeb..5f74b2a 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -82,9 +82,6 @@
     // Input event dispatching is paused.
     public boolean paused;
 
-    // Window layer.
-    public int layer;
-
     // Id of process and user that owns the window.
     public int ownerPid;
     public int ownerUid;
@@ -126,7 +123,6 @@
     @Override
     public String toString() {
         return new StringBuilder(name != null ? name : "")
-                .append(", layer=").append(layer)
                 .append(", frame=[").append(frameLeft).append(",").append(frameTop).append(",")
                         .append(frameRight).append(",").append(frameBottom).append("]")
                 .append(", touchableRegion=").append(touchableRegion)
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 1a33ea9..324d562 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -41,6 +41,7 @@
     public InsetsSource(@InternalInsetsType int type) {
         mType = type;
         mFrame = new Rect();
+        mVisible = InsetsState.getDefaultVisibility(type);
     }
 
     public InsetsSource(InsetsSource other) {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index c3c7b95..0068476 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1541,6 +1541,8 @@
     @FastNative
     private static native float nativeGetAxisValue(long nativePtr,
             int axis, int pointerIndex, int historyPos);
+    @FastNative
+    private static native void nativeTransform(long nativePtr, Matrix matrix);
 
     // -------------- @CriticalNative ----------------------
 
@@ -1614,8 +1616,6 @@
 
     @CriticalNative
     private static native void nativeScale(long nativePtr, float scale);
-    @CriticalNative
-    private static native void nativeTransform(long nativePtr, long matrix);
 
     private MotionEvent() {
     }
@@ -3210,7 +3210,7 @@
             throw new IllegalArgumentException("matrix must not be null");
         }
 
-        nativeTransform(mNativePtr, matrix.native_instance);
+        nativeTransform(mNativePtr, matrix);
     }
 
     /**
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 64c8777..cfd624a 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -68,7 +68,7 @@
     }
 
     /**
-     * Checks is this is a text value.
+     * Checks if this is a text value.
      *
      * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
      */
@@ -89,7 +89,7 @@
     }
 
     /**
-     * Checks is this is a toggle value.
+     * Checks if this is a toggle value.
      *
      * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
      */
@@ -110,7 +110,7 @@
     }
 
     /**
-     * Checks is this is a list value.
+     * Checks if this is a list value.
      *
      * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
      */
@@ -131,7 +131,7 @@
     }
 
     /**
-     * Checks is this is a date value.
+     * Checks if this is a date value.
      *
      * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
      */
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 1e5a3b0..cf494ae 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -63,6 +63,8 @@
  * which is what clients use to communicate with the input method.
  */
 public interface InputMethod {
+    /** @hide **/
+    public static final String TAG = "InputMethod";
     /**
      * This is the interface name that a service implementing an input
      * method should say that it supports -- that is, this is the action it
@@ -111,6 +113,10 @@
     /**
      * Called to notify the IME that Autofill Frameworks requested an inline suggestions request.
      *
+     * @param componentName {@link ComponentName} of current app/activity.
+     * @param autofillId {@link AutofillId} of currently focused field.
+     * @param cb {@link IInlineSuggestionsRequestCallback} used to pass back the request object.
+     *
      * @hide
      */
     default void onCreateInlineSuggestionsRequest(ComponentName componentName,
@@ -118,7 +124,7 @@
         try {
             cb.onInlineSuggestionsUnsupported();
         } catch (RemoteException e) {
-            Log.w("InputMethod", "RemoteException calling onInlineSuggestionsUnsupported: " + e);
+            Log.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
         }
     }
 
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 34005ac..2864485 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -58,6 +58,7 @@
  * @attr ref android.R.styleable#InputMethod_settingsActivity
  * @attr ref android.R.styleable#InputMethod_isDefault
  * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
+ * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
  */
 public final class InputMethodInfo implements Parcelable {
     static final String TAG = "InputMethodInfo";
@@ -111,6 +112,11 @@
     private final boolean mSupportsSwitchingToNextInputMethod;
 
     /**
+     * The flag whether this IME supports inline suggestions.
+     */
+    private final boolean mInlineSuggestionsEnabled;
+
+    /**
      * @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
      * @return a unique ID to be returned by {@link #getId()}. We have used
      *         {@link ComponentName#flattenToShortString()} for this purpose (and it is already
@@ -152,6 +158,7 @@
         mId = computeId(service);
         boolean isAuxIme = true;
         boolean supportsSwitchingToNextInputMethod = false; // false as default
+        boolean inlineSuggestionsEnabled = false; // false as default
         mForceDefault = false;
 
         PackageManager pm = context.getPackageManager();
@@ -193,6 +200,8 @@
             supportsSwitchingToNextInputMethod = sa.getBoolean(
                     com.android.internal.R.styleable.InputMethod_supportsSwitchingToNextInputMethod,
                     false);
+            inlineSuggestionsEnabled = sa.getBoolean(
+                    com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
             sa.recycle();
 
             final int depth = parser.getDepth();
@@ -263,6 +272,7 @@
         mIsDefaultResId = isDefaultResId;
         mIsAuxIme = isAuxIme;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
         mIsVrOnly = isVrOnly;
     }
 
@@ -272,6 +282,7 @@
         mIsDefaultResId = source.readInt();
         mIsAuxIme = source.readInt() == 1;
         mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
+        mInlineSuggestionsEnabled = source.readInt() == 1;
         mIsVrOnly = source.readBoolean();
         mService = ResolveInfo.CREATOR.createFromParcel(source);
         mSubtypes = new InputMethodSubtypeArray(source);
@@ -286,7 +297,7 @@
         this(buildDummyResolveInfo(packageName, className, label), false /* isAuxIme */,
                 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */,
                 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
-                false /* isVrOnly */);
+                false /* inlineSuggestionsEnabled */, false /* isVrOnly */);
     }
 
     /**
@@ -297,7 +308,8 @@
             String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
             boolean forceDefault) {
         this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
-                true /* supportsSwitchingToNextInputMethod */, false /* isVrOnly */);
+                true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
+                false /* isVrOnly */);
     }
 
     /**
@@ -307,6 +319,18 @@
     public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
             List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
             boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
+        this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault,
+                supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly);
+    }
+
+    /**
+     * Temporary API for creating a built-in input method for test.
+     * @hide
+     */
+    public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
+            List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
+            boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
+            boolean isVrOnly) {
         final ServiceInfo si = ri.serviceInfo;
         mService = ri;
         mId = new ComponentName(si.packageName, si.name).flattenToShortString();
@@ -316,6 +340,7 @@
         mSubtypes = new InputMethodSubtypeArray(subtypes);
         mForceDefault = forceDefault;
         mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
+        mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
         mIsVrOnly = isVrOnly;
     }
 
@@ -467,7 +492,8 @@
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
                 + " mIsVrOnly=" + mIsVrOnly
-                + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
+                + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+                + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
         pw.println(prefix + "Service:");
@@ -528,6 +554,14 @@
     }
 
     /**
+     * @return true if this input method supports inline suggestions.
+     * @hide
+     */
+    public boolean isInlineSuggestionsEnabled() {
+        return mInlineSuggestionsEnabled;
+    }
+
+    /**
      * Used to package this object into a {@link Parcel}.
      *
      * @param dest The {@link Parcel} to be written.
@@ -540,6 +574,7 @@
         dest.writeInt(mIsDefaultResId);
         dest.writeInt(mIsAuxIme ? 1 : 0);
         dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
+        dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0);
         dest.writeBoolean(mIsVrOnly);
         mService.writeToParcel(dest, flags);
         mSubtypes.writeToParcel(dest);
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index abfbc6c..4329a20 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -20,6 +20,7 @@
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 
 import com.android.internal.util.Preconditions;
+
 import java.util.Objects;
 
 /**
@@ -183,6 +184,7 @@
                     mSmartEvent = event;
                     break;
                 case SelectionEvent.ACTION_ABANDON:
+                case SelectionEvent.ACTION_OVERTYPE:
                     if (mPrevEvent != null) {
                         event.setEntityType(mPrevEvent.getEntityType());
                     }
diff --git a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 7dbcbf9..28cb80d 100644
--- a/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/core/java/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -22,6 +22,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.metrics.LogMaker;
+import android.os.Build;
 import android.util.Log;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassifier;
@@ -101,7 +102,8 @@
     private boolean mSmartSelectionTriggered;
     private String mModelName;
 
-    @UnsupportedAppUsage(trackingBug = 136637107)
+    @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
     public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
         mWidgetType = widgetType;
         mWidgetVersion = null;
@@ -120,7 +122,8 @@
      *
      * @param event the selection event
      */
-    @UnsupportedAppUsage(trackingBug = 136637107)
+    @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
     public void logEvent(@NonNull SelectionEvent event) {
         Objects.requireNonNull(event);
 
@@ -444,7 +447,8 @@
          *
          * @param start  the word index of the selected word
          */
-        @UnsupportedAppUsage(trackingBug = 136637107)
+        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
         public static SelectionEvent selectionStarted(int start) {
             return new SelectionEvent(
                     start, start + 1, EventType.SELECTION_STARTED,
@@ -458,7 +462,8 @@
          * @param start  the start word (inclusive) index of the selection
          * @param end  the end word (exclusive) index of the selection
          */
-        @UnsupportedAppUsage(trackingBug = 136637107)
+        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
         public static SelectionEvent selectionModified(int start, int end) {
             return new SelectionEvent(
                     start, end, EventType.SELECTION_MODIFIED,
@@ -474,7 +479,8 @@
          * @param classification  the TextClassification object returned by the TextClassifier that
          *      classified the selected text
          */
-        @UnsupportedAppUsage(trackingBug = 136637107)
+        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
         public static SelectionEvent selectionModified(
                 int start, int end, @NonNull TextClassification classification) {
             final String entityType = classification.getEntityCount() > 0
@@ -494,7 +500,8 @@
          * @param selection  the TextSelection object returned by the TextClassifier for the
          *      specified selection
          */
-        @UnsupportedAppUsage(trackingBug = 136637107)
+        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
         public static SelectionEvent selectionModified(
                 int start, int end, @NonNull TextSelection selection) {
             final boolean smartSelection = getSourceClassifier(selection.getId())
@@ -523,7 +530,8 @@
          * @param end  the end word (exclusive) index of the selection
          * @param actionType  the action that was performed on the selection
          */
-        @UnsupportedAppUsage(trackingBug = 136637107)
+        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
         public static SelectionEvent selectionAction(
                 int start, int end, @ActionType int actionType) {
             return new SelectionEvent(
@@ -541,7 +549,8 @@
          * @param classification  the TextClassification object returned by the TextClassifier that
          *      classified the selected text
          */
-        @UnsupportedAppUsage(trackingBug = 136637107)
+        @UnsupportedAppUsage(trackingBug = 136637107, maxTargetSdk = Build.VERSION_CODES.Q,
+                publicAlternatives = "See {@link android.view.textclassifier.TextClassifier}.")
         public static SelectionEvent selectionAction(
                 int start, int end, @ActionType int actionType,
                 @NonNull TextClassification classification) {
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 547aad6..5820f4b 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -19,7 +19,7 @@
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index e250f63..600a34c 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -16,8 +16,8 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 0c593be..c1c1a6e 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -19,8 +19,8 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 5f15110..096e6ea 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/DateTimeView.java b/core/java/android/widget/DateTimeView.java
index 2864ad0..20a53c0 100644
--- a/core/java/android/widget/DateTimeView.java
+++ b/core/java/android/widget/DateTimeView.java
@@ -21,8 +21,8 @@
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index fa0af78..32f3acd 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -18,7 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.BlendMode;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3b6a009..a0cf534 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -21,10 +21,10 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.app.RemoteAction;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipData.Item;
 import android.content.Context;
@@ -139,6 +139,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Helper class used by TextView to handle editable text views.
@@ -1461,7 +1462,11 @@
         return false;
     }
 
-    void onTouchEvent(MotionEvent event) {
+    /**
+     * Handles touch events on an editable text view, implementing cursor movement, selection, etc.
+     */
+    @VisibleForTesting
+    public void onTouchEvent(MotionEvent event) {
         final boolean filterOutEvent = shouldFilterOutTouchEvent(event);
         mLastButtonState = event.getButtonState();
         if (filterOutEvent) {
@@ -2423,7 +2428,9 @@
         return mSelectionControllerEnabled;
     }
 
-    private InsertionPointCursorController getInsertionController() {
+    /** Returns the controller for the insertion cursor. */
+    @VisibleForTesting
+    public @Nullable InsertionPointCursorController getInsertionController() {
         if (!mInsertionControllerEnabled) {
             return null;
         }
@@ -2438,8 +2445,9 @@
         return mInsertionPointCursorController;
     }
 
-    @Nullable
-    SelectionModifierCursorController getSelectionController() {
+    /** Returns the controller for selection. */
+    @VisibleForTesting
+    public @Nullable SelectionModifierCursorController getSelectionController() {
         if (!mSelectionControllerEnabled) {
             return null;
         }
@@ -5722,11 +5730,16 @@
         }
     }
 
-    class InsertionPointCursorController implements CursorController {
+    /** Controller for the insertion cursor. */
+    @VisibleForTesting
+    public class InsertionPointCursorController implements CursorController {
         private InsertionHandleView mHandle;
         private boolean mIsDraggingCursor;
 
         public void onTouchEvent(MotionEvent event) {
+            if (getSelectionController().isCursorBeingModified()) {
+                return;
+            }
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
                     mIsDraggingCursor = false;
@@ -5737,7 +5750,8 @@
                     } else if (FLAG_ENABLE_CURSOR_DRAG
                                 && mTextView.getLayout() != null
                                 && mTextView.isFocused()
-                                && mTouchState.isMovedEnoughForDrag()) {
+                                && mTouchState.isMovedEnoughForDrag()
+                                && !mTouchState.isDragCloseToVertical()) {
                         startCursorDrag(event);
                     }
                     break;
@@ -5898,7 +5912,9 @@
         }
     }
 
-    class SelectionModifierCursorController implements CursorController {
+    /** Controller for selection. */
+    @VisibleForTesting
+    public class SelectionModifierCursorController implements CursorController {
         // The cursor controller handles, lazily created when shown.
         private SelectionHandleView mStartHandle;
         private SelectionHandleView mEndHandle;
@@ -7062,11 +7078,11 @@
         private final List<ResolveInfo> mSupportedActivities = new ArrayList<>();
 
         private ProcessTextIntentActionsHandler(Editor editor) {
-            mEditor = Preconditions.checkNotNull(editor);
-            mTextView = Preconditions.checkNotNull(mEditor.mTextView);
-            mContext = Preconditions.checkNotNull(mTextView.getContext());
-            mPackageManager = Preconditions.checkNotNull(mContext.getPackageManager());
-            mPackageName = Preconditions.checkNotNull(mContext.getPackageName());
+            mEditor = Objects.requireNonNull(editor);
+            mTextView = Objects.requireNonNull(mEditor.mTextView);
+            mContext = Objects.requireNonNull(mTextView.getContext());
+            mPackageManager = Objects.requireNonNull(mContext.getPackageManager());
+            mPackageName = Objects.requireNonNull(mContext.getPackageName());
         }
 
         /**
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index 3798d00..d53099d 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -56,6 +56,7 @@
     private boolean mMultiTapInSameArea;
 
     private boolean mMovedEnoughForDrag;
+    private boolean mIsDragCloseToVertical;
 
     public float getLastDownX() {
         return mLastDownX;
@@ -94,6 +95,10 @@
         return mMovedEnoughForDrag;
     }
 
+    public boolean isDragCloseToVertical() {
+        return mIsDragCloseToVertical;
+    }
+
     /**
      * Updates the state based on the new event.
      */
@@ -129,6 +134,7 @@
             mLastDownX = event.getX();
             mLastDownY = event.getY();
             mMovedEnoughForDrag = false;
+            mIsDragCloseToVertical = false;
         } else if (action == MotionEvent.ACTION_UP) {
             if (TextView.DEBUG_CURSOR) {
                 logCursor("EditorTouchState", "ACTION_UP");
@@ -137,9 +143,24 @@
             mLastUpY = event.getY();
             mLastUpMillis = event.getEventTime();
             mMovedEnoughForDrag = false;
+            mIsDragCloseToVertical = false;
         } else if (action == MotionEvent.ACTION_MOVE) {
-            mMovedEnoughForDrag = !isDistanceWithin(mLastDownX, mLastDownY,
-                    event.getX(), event.getY(), config.getScaledTouchSlop());
+            if (!mMovedEnoughForDrag) {
+                float deltaX = event.getX() - mLastDownX;
+                float deltaY = event.getY() - mLastDownY;
+                float deltaXSquared = deltaX * deltaX;
+                float distanceSquared = (deltaXSquared) + (deltaY * deltaY);
+                int touchSlop = config.getScaledTouchSlop();
+                mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
+                if (mMovedEnoughForDrag) {
+                    // If the direction of the swipe motion is within 30 degrees of vertical, it is
+                    // considered a vertical drag. We don't actually have to compute the angle to
+                    // implement the check though. When the angle is exactly 30 degrees from
+                    // vertical, 2*deltaX = distance. When the angle is less than 30 degrees from
+                    // vertical, 2*deltaX < distance.
+                    mIsDragCloseToVertical = (4 * deltaXSquared) <= distanceSquared;
+                }
+            }
         }
     }
 
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index cae91fc..bdfb550 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -18,7 +18,7 @@
 
 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 2c09185..0c0b349 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -23,7 +23,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/Filter.java b/core/java/android/widget/Filter.java
index 16f4ee2..06e6a5a 100644
--- a/core/java/android/widget/Filter.java
+++ b/core/java/android/widget/Filter.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 69da911..92e9a96 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 64192aa..8c0061d 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -17,8 +17,8 @@
 package android.widget;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index 8cda47d..f132197 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -31,7 +31,7 @@
 import static java.lang.Math.min;
 
 import android.annotation.IntDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 4e39a55..4a5e95e 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -18,7 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/HeaderViewListAdapter.java b/core/java/android/widget/HeaderViewListAdapter.java
index 10d50b8..eda7580 100644
--- a/core/java/android/widget/HeaderViewListAdapter.java
+++ b/core/java/android/widget/HeaderViewListAdapter.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.database.DataSetObserver;
 import android.view.View;
 import android.view.ViewGroup;
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index ec685f5..b33660a 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -18,7 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index d62b979..c2077384 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.ColorStateList;
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index a83e826..a796ba5 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index eb7a75b..8595fec 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.DataSetObserver;
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 3a197e2..79ec680 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -19,7 +19,7 @@
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index f58b6d1..2924dd9 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -62,6 +62,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * Android magnifier widget. Can be used by any view which is attached to a window.
@@ -1154,7 +1155,7 @@
          * @param view the view this magnifier is attached to
          */
         public Builder(@NonNull View view) {
-            mView = Preconditions.checkNotNull(view);
+            mView = Objects.requireNonNull(view);
             applyDefaults();
         }
 
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 65925b4..9c9baf3 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.PixelFormat;
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 882e81a..e9e0c14 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -23,8 +23,8 @@
 import android.annotation.IntRange;
 import android.annotation.Px;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index e7a96be..1c33d80 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.hardware.SensorManager;
 import android.util.Log;
diff --git a/core/java/android/widget/PopupMenu.java b/core/java/android/widget/PopupMenu.java
index b0c0c12..0ce9646 100644
--- a/core/java/android/widget/PopupMenu.java
+++ b/core/java/android/widget/PopupMenu.java
@@ -18,7 +18,7 @@
 
 import android.annotation.MenuRes;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.view.Gravity;
 import android.view.Menu;
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 3779779..bf696f5 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -18,13 +18,12 @@
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams
-        .PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.PixelFormat;
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 5cc0e0e..733a775 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Px;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index c1a217c..ea39f6d 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.AsyncQueryHandler;
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index 90bc0a3..71ccb59 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -19,7 +19,7 @@
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
diff --git a/core/java/android/widget/RatingBar.java b/core/java/android/widget/RatingBar.java
index 3cf3d91..f946fe6 100644
--- a/core/java/android/widget/RatingBar.java
+++ b/core/java/android/widget/RatingBar.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.shapes.RectShape;
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 253b6e1..d085eda 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -19,7 +19,7 @@
 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.ResourceId;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index a6304b1..d86766e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -22,7 +22,6 @@
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -30,6 +29,7 @@
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.appwidget.AppWidgetHostView;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index efc5eb3..e58f08a 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -19,11 +19,11 @@
 import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID;
 import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND;
 
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.app.IServiceConnection;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/widget/ScrollBarDrawable.java b/core/java/android/widget/ScrollBarDrawable.java
index a5d3e45..80e17b5 100644
--- a/core/java/android/widget/ScrollBarDrawable.java
+++ b/core/java/android/widget/ScrollBarDrawable.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 1cc8ff6..3847d6b 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -18,7 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index 229eaf0..6ed5b7e 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.hardware.SensorManager;
 import android.os.Build;
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 89d9e97..15959c2 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -19,10 +19,10 @@
 import static android.widget.SuggestionsAdapter.getColumnString;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/core/java/android/widget/SeekBar.java b/core/java/android/widget/SeekBar.java
index e8cf1e8..5676881 100644
--- a/core/java/android/widget/SeekBar.java
+++ b/core/java/android/widget/SeekBar.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.accessibility.AccessibilityNodeInfo;
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index d0f8093..4ef3f61 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -87,7 +87,7 @@
     private final SmartSelectSprite mSmartSelectSprite;
 
     SelectionActionModeHelper(@NonNull Editor editor) {
-        mEditor = Preconditions.checkNotNull(editor);
+        mEditor = Objects.requireNonNull(editor);
         mTextView = mEditor.getTextView();
         mTextClassificationHelper = new TextClassificationHelper(
                 mTextView.getContext(),
@@ -500,7 +500,7 @@
         private final LogAbandonRunnable mDelayedLogAbandon = new LogAbandonRunnable();
 
         SelectionTracker(TextView textView) {
-            mTextView = Preconditions.checkNotNull(textView);
+            mTextView = Objects.requireNonNull(textView);
             mLogger = new SelectionMetricsLogger(textView);
         }
 
@@ -703,7 +703,7 @@
         private String mText;
 
         SelectionMetricsLogger(TextView textView) {
-            Preconditions.checkNotNull(textView);
+            Objects.requireNonNull(textView);
             mEditTextLogger = textView.isTextEditable();
             mTokenIterator = SelectionSessionLogger.getTokenIterator(textView.getTextLocale());
         }
@@ -714,7 +714,7 @@
                 CharSequence text, int index,
                 @InvocationMethod int invocationMethod) {
             try {
-                Preconditions.checkNotNull(text);
+                Objects.requireNonNull(text);
                 Preconditions.checkArgumentInRange(index, 0, text.length(), "index");
                 if (mText == null || !mText.contentEquals(text)) {
                     mText = text.toString();
@@ -972,11 +972,11 @@
                 @NonNull Consumer<SelectionResult> selectionResultCallback,
                 @NonNull Supplier<SelectionResult> timeOutResultSupplier) {
             super(textView != null ? textView.getHandler() : null);
-            mTextView = Preconditions.checkNotNull(textView);
+            mTextView = Objects.requireNonNull(textView);
             mTimeOutDuration = timeOut;
-            mSelectionResultSupplier = Preconditions.checkNotNull(selectionResultSupplier);
-            mSelectionResultCallback = Preconditions.checkNotNull(selectionResultCallback);
-            mTimeOutResultSupplier = Preconditions.checkNotNull(timeOutResultSupplier);
+            mSelectionResultSupplier = Objects.requireNonNull(selectionResultSupplier);
+            mSelectionResultCallback = Objects.requireNonNull(selectionResultCallback);
+            mTimeOutResultSupplier = Objects.requireNonNull(timeOutResultSupplier);
             // Make a copy of the original text.
             mOriginalText = getText(mTextView).toString();
         }
@@ -1051,14 +1051,14 @@
         TextClassificationHelper(Context context, Supplier<TextClassifier> textClassifier,
                 CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
             init(textClassifier, text, selectionStart, selectionEnd, locales);
-            mContext = Preconditions.checkNotNull(context);
+            mContext = Objects.requireNonNull(context);
         }
 
         @UiThread
         public void init(Supplier<TextClassifier> textClassifier, CharSequence text,
                 int selectionStart, int selectionEnd, LocaleList locales) {
-            mTextClassifier = Preconditions.checkNotNull(textClassifier);
-            mText = Preconditions.checkNotNull(text).toString();
+            mTextClassifier = Objects.requireNonNull(textClassifier);
+            mText = Objects.requireNonNull(text).toString();
             mLastClassificationText = null; // invalidate.
             Preconditions.checkArgument(selectionEnd > selectionStart);
             mSelectionStart = selectionStart;
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index 15e1ffa..404b817 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -18,7 +18,7 @@
 
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.Uri;
diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java
index 77fe5d1..6277d5b 100644
--- a/core/java/android/widget/SimpleCursorAdapter.java
+++ b/core/java/android/widget/SimpleCursorAdapter.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java
index 2ab2b24..9a4cfa1 100644
--- a/core/java/android/widget/SlidingDrawer.java
+++ b/core/java/android/widget/SlidingDrawer.java
@@ -17,7 +17,7 @@
 package android.widget;
 
 import android.R;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 9a84f69..dc472e1 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -38,13 +38,12 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A utility class for creating and animating the Smart Select animation.
@@ -75,7 +74,7 @@
         private final int mTextSelectionLayout;
 
         RectangleWithTextSelectionLayout(RectF rectangle, int textSelectionLayout) {
-            mRectangle = Preconditions.checkNotNull(rectangle);
+            mRectangle = Objects.requireNonNull(rectangle);
             mTextSelectionLayout = textSelectionLayout;
         }
 
@@ -342,7 +341,7 @@
                 context,
                 android.R.interpolator.fast_out_linear_in);
         mFillColor = highlightColor;
-        mInvalidator = Preconditions.checkNotNull(invalidator);
+        mInvalidator = Objects.requireNonNull(invalidator);
     }
 
     /**
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 92fcea3..46fc09f 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -19,9 +19,9 @@
 import android.annotation.DrawableRes;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
 import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index fbd29ba..e1fd776 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index 45e635ebe..a9a35115 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -18,8 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.LocalActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java
index bd0d039..36abf3a 100644
--- a/core/java/android/widget/TabWidget.java
+++ b/core/java/android/widget/TabWidget.java
@@ -18,7 +18,7 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index 2ae4389..5731e50 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -21,8 +21,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 43d9895..32d3fef 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -37,11 +37,11 @@
 import android.annotation.Size;
 import android.annotation.StringRes;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.XmlRes;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.app.assist.AssistStructure;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ClipboardManager;
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 8a5d531..51b1847 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -20,8 +20,8 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.icu.util.Calendar;
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8f2133f..9bdb4c1 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -20,9 +20,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
-import android.annotation.UnsupportedAppUsage;
 import android.app.INotificationManager;
 import android.app.ITransientNotification;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index a21fb41..ea3f06b 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -24,8 +24,8 @@
 import android.annotation.StringRes;
 import android.annotation.StyleRes;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActionBar;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java
index 40b0f13..36dafd5 100644
--- a/core/java/android/widget/VideoView.java
+++ b/core/java/android/widget/VideoView.java
@@ -17,8 +17,8 @@
 package android.widget;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
 import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java
index d36f343..90f61ca 100644
--- a/core/java/android/widget/ViewAnimator.java
+++ b/core/java/android/widget/ViewAnimator.java
@@ -18,7 +18,7 @@
 
 
 import android.annotation.AnimRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
diff --git a/core/java/android/widget/ViewFlipper.java b/core/java/android/widget/ViewFlipper.java
index b962298..2df9a78 100644
--- a/core/java/android/widget/ViewFlipper.java
+++ b/core/java/android/widget/ViewFlipper.java
@@ -17,7 +17,7 @@
 package android.widget;
 
 import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java
index 7a5b7e8..f435533 100644
--- a/core/java/android/widget/ZoomControls.java
+++ b/core/java/android/widget/ZoomControls.java
@@ -16,8 +16,8 @@
 
 package android.widget;
 
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.Widget;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
diff --git a/core/java/com/android/ims/internal/uce/common/CapInfo.java b/core/java/com/android/ims/internal/uce/common/CapInfo.java
index a9847ba..c45a3a4 100644
--- a/core/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/core/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -16,10 +16,9 @@
 
 package com.android.ims.internal.uce.common;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 /** Class for capability discovery information.
  *  @hide */
diff --git a/core/java/com/android/ims/internal/uce/common/StatusCode.java b/core/java/com/android/ims/internal/uce/common/StatusCode.java
index 7250eee..7f69493 100644
--- a/core/java/com/android/ims/internal/uce/common/StatusCode.java
+++ b/core/java/com/android/ims/internal/uce/common/StatusCode.java
@@ -16,10 +16,9 @@
 
 package com.android.ims.internal.uce.common;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 
 /** Class for UCE status codes.
diff --git a/core/java/com/android/ims/internal/uce/common/UceLong.java b/core/java/com/android/ims/internal/uce/common/UceLong.java
index 7207899..bf51447 100644
--- a/core/java/com/android/ims/internal/uce/common/UceLong.java
+++ b/core/java/com/android/ims/internal/uce/common/UceLong.java
@@ -16,10 +16,9 @@
 
 package com.android.ims.internal.uce.common;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
 
 
 /** Simple object wrapper for a long type.
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
index bcb9f2d..1da5a24 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
@@ -15,11 +15,11 @@
  */
 package com.android.ims.internal.uce.options;
 
-import android.annotation.UnsupportedAppUsage;
-import com.android.ims.internal.uce.common.CapInfo;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Log;
+
+import com.android.ims.internal.uce.common.CapInfo;
 
 /** @hide  */
 public class OptionsCapInfo implements Parcelable {
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
index 14c64ac..401ca2f 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCmdId.java
@@ -17,7 +17,7 @@
 package com.android.ims.internal.uce.options;
 
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
index 4af3e6e..70a7a84 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
@@ -16,13 +16,13 @@
 
 package com.android.ims.internal.uce.options;
 
-import com.android.ims.internal.uce.common.StatusCode;
-import com.android.ims.internal.uce.common.CapInfo;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.ims.internal.uce.common.CapInfo;
+import com.android.ims.internal.uce.common.StatusCode;
+
 /** @hide  */
 public class OptionsCmdStatus implements Parcelable {
 
diff --git a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index c5f333d..5afddf0 100644
--- a/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.options;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
index 745df5b..1a3a028 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCapInfo.java
@@ -16,12 +16,12 @@
 
 package com.android.ims.internal.uce.presence;
 
-import com.android.ims.internal.uce.common.CapInfo;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.ims.internal.uce.common.CapInfo;
+
 
 /** @hide */
 public class PresCapInfo implements Parcelable {
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCmdId.java b/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
index 41020ec..fba0c77 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCmdId.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
index ff8069c..fbc64b8 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
@@ -16,12 +16,12 @@
 
 package com.android.ims.internal.uce.presence;
 
-import com.android.ims.internal.uce.common.StatusCode;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.ims.internal.uce.common.StatusCode;
+
 
 /** @hide  */
 public class PresCmdStatus implements Parcelable{
diff --git a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index 87193e3..a50a22f 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
index 237c999..af9b056 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInfo.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 29699ea..9f37251 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -16,9 +16,10 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
+
 import java.util.Arrays;
 
 /** @hide  */
diff --git a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
index ab46e4b..65b9fdb 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java b/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
index 83ba722..5eafa0f 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 5e42592..45b02f3 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java b/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
index bee928c..ab1e17c 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index 7a47786..3608eb6a 100644
--- a/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/core/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -16,7 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 33aa665..f9ba34e 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -15,14 +15,20 @@
  */
 package com.android.internal.app;
 
+import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
+import static android.view.accessibility.AccessibilityManager.ShortcutType;
+
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.view.LayoutInflater;
@@ -30,12 +36,17 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.accessibility.AccessibilityManager;
+import android.widget.AdapterView;
 import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.Switch;
 import android.widget.TextView;
 
 import com.android.internal.R;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -48,40 +59,74 @@
     private static final String MAGNIFICATION_COMPONENT_ID =
             "com.android.server.accessibility.MagnificationController";
 
-    private AccessibilityButtonTarget mMagnificationTarget = null;
-
-    private List<AccessibilityButtonTarget> mTargets = null;
-
+    private int mShortcutType;
+    private List<AccessibilityButtonTarget> mTargets = new ArrayList<>();
+    private List<AccessibilityButtonTarget> mReadyToBeDisabledTargets = new ArrayList<>();
     private AlertDialog mAlertDialog;
+    private TargetAdapter mTargetAdapter;
+
+    /**
+     * Annotation for different accessibilityService fragment UI type.
+     *
+     * {@code LEGACY} for displaying appearance aligned with sdk version Q accessibility service
+     * page, but only hardware shortcut allowed and under service in version Q or early.
+     * {@code INVISIBLE} for displaying appearance without switch bar.
+     * {@code INTUITIVE} for displaying appearance with version R accessibility design.
+     * {@code BOUNCE} for displaying appearance with pop-up action.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            AccessibilityServiceFragmentType.LEGACY,
+            AccessibilityServiceFragmentType.INVISIBLE,
+            AccessibilityServiceFragmentType.INTUITIVE,
+            AccessibilityServiceFragmentType.BOUNCE,
+    })
+    public @interface AccessibilityServiceFragmentType {
+        int LEGACY = 0;
+        int INVISIBLE = 1;
+        int INTUITIVE = 2;
+        int BOUNCE = 3;
+    }
+
+    /**
+     * Annotation for different shortcut menu mode.
+     *
+     * {@code LAUNCH} for clicking list item to trigger the service callback.
+     * {@code EDIT} for clicking list item and save button to disable the service.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            ShortcutMenuMode.LAUNCH,
+            ShortcutMenuMode.EDIT,
+    })
+    public @interface ShortcutMenuMode {
+        int LAUNCH = 0;
+        int EDIT = 1;
+    }
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
-        if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, /* defValue= */false)) {
+        if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, /* defValue= */ false)) {
             requestWindowFeature(Window.FEATURE_NO_TITLE);
         }
 
-        // TODO(b/146815874): Will replace it with white list services
-        mMagnificationTarget = new AccessibilityButtonTarget(this, MAGNIFICATION_COMPONENT_ID,
-                R.string.accessibility_magnification_chooser_text,
-                R.drawable.ic_accessibility_magnification);
-
-        // TODO(b/146815544): Will use shortcut type or button type to get the corresponding
-        //  services
-        mTargets = getServiceAccessibilityButtonTargets(this);
-        if (Settings.Secure.getInt(getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) {
-            mTargets.add(mMagnificationTarget);
-        }
+        mShortcutType = getIntent().getIntExtra(AccessibilityManager.EXTRA_SHORTCUT_TYPE,
+                ACCESSIBILITY_BUTTON);
+        mTargets.addAll(getServiceTargets(this, mShortcutType));
 
         // TODO(b/146815548): Will add title to separate which one type
+        mTargetAdapter = new TargetAdapter(mTargets);
         mAlertDialog = new AlertDialog.Builder(this)
-                .setAdapter(new TargetAdapter(),
-                        (dialog, position) -> onTargetSelected(mTargets.get(position)))
+                .setAdapter(mTargetAdapter, /* listener= */ null)
+                .setPositiveButton(
+                        getString(R.string.edit_accessibility_shortcut_menu_button),
+                        /* listener= */ null)
                 .setOnDismissListener(dialog -> finish())
                 .create();
+        mAlertDialog.setOnShowListener(dialog -> updateDialogListeners());
         mAlertDialog.show();
     }
 
@@ -91,41 +136,95 @@
         super.onDestroy();
     }
 
-    private static List<AccessibilityButtonTarget> getServiceAccessibilityButtonTargets(
-            @NonNull Context context) {
-        AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
+    /**
+     * Gets the corresponding fragment type of a given accessibility service.
+     *
+     * @param accessibilityServiceInfo The accessibilityService's info.
+     * @return int from {@link AccessibilityServiceFragmentType}.
+     */
+    private static @AccessibilityServiceFragmentType int getAccessibilityServiceFragmentType(
+            AccessibilityServiceInfo accessibilityServiceInfo) {
+        final int targetSdk = accessibilityServiceInfo.getResolveInfo()
+                .serviceInfo.applicationInfo.targetSdkVersion;
+        final boolean requestA11yButton = (accessibilityServiceInfo.flags
+                & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0;
+
+        if (targetSdk <= Build.VERSION_CODES.Q) {
+            return AccessibilityServiceFragmentType.LEGACY;
+        }
+        return requestA11yButton
+                ? AccessibilityServiceFragmentType.INVISIBLE
+                : AccessibilityServiceFragmentType.INTUITIVE;
+    }
+
+    private static List<AccessibilityButtonTarget> getServiceTargets(@NonNull Context context,
+            @ShortcutType int shortcutType) {
+        final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
                 Context.ACCESSIBILITY_SERVICE);
-        List<AccessibilityServiceInfo> services = ams.getEnabledAccessibilityServiceList(
-                AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
-        if (services == null) {
+        final List<AccessibilityServiceInfo> installedServices =
+                ams.getInstalledAccessibilityServiceList();
+        if (installedServices == null) {
             return Collections.emptyList();
         }
 
-        ArrayList<AccessibilityButtonTarget> targets = new ArrayList<>(services.size());
-        for (AccessibilityServiceInfo info : services) {
+        final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
+        for (AccessibilityServiceInfo info : installedServices) {
             if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
                 targets.add(new AccessibilityButtonTarget(context, info));
             }
         }
 
+        final List<String> requiredTargets = ams.getAccessibilityShortcutTargets(shortcutType);
+        targets.removeIf(target -> !requiredTargets.contains(target.getId()));
+
+        // TODO(b/146815874): Will replace it with white list services.
+        if (Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) {
+            final AccessibilityButtonTarget magnificationTarget = new AccessibilityButtonTarget(
+                    context,
+                    MAGNIFICATION_COMPONENT_ID,
+                    R.string.accessibility_magnification_chooser_text,
+                    R.drawable.ic_accessibility_magnification,
+                    AccessibilityServiceFragmentType.INTUITIVE);
+            targets.add(magnificationTarget);
+        }
+
         return targets;
     }
 
-    private void onTargetSelected(AccessibilityButtonTarget target) {
-        Settings.Secure.putString(getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, target.getId());
-        finish();
+    private static class ViewHolder {
+        ImageView mIconView;
+        TextView mLabelView;
+        FrameLayout mItemContainer;
+        ImageView mViewItem;
+        Switch mSwitchItem;
     }
 
-    private class TargetAdapter extends BaseAdapter {
+    private static class TargetAdapter extends BaseAdapter {
+        @ShortcutMenuMode
+        private int mShortcutMenuMode = ShortcutMenuMode.LAUNCH;
+        private List<AccessibilityButtonTarget> mButtonTargets;
+
+        TargetAdapter(List<AccessibilityButtonTarget> targets) {
+            this.mButtonTargets = targets;
+        }
+
+        void setShortcutMenuMode(int shortcutMenuMode) {
+            mShortcutMenuMode = shortcutMenuMode;
+        }
+
+        int getShortcutMenuMode() {
+            return mShortcutMenuMode;
+        }
+
         @Override
         public int getCount() {
-            return mTargets.size();
+            return mButtonTargets.size();
         }
 
         @Override
         public Object getItem(int position) {
-            return null;
+            return mButtonTargets.get(position);
         }
 
         @Override
@@ -135,36 +234,110 @@
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            LayoutInflater inflater = AccessibilityButtonChooserActivity.this.getLayoutInflater();
-            View root = inflater.inflate(R.layout.accessibility_button_chooser_item, parent, false);
-            final AccessibilityButtonTarget target = mTargets.get(position);
-            ImageView iconView = root.findViewById(R.id.accessibility_button_target_icon);
-            TextView labelView = root.findViewById(R.id.accessibility_button_target_label);
-            iconView.setImageDrawable(target.getDrawable());
-            labelView.setText(target.getLabel());
+            final Context context = parent.getContext();
+            ViewHolder holder;
+            if (convertView == null) {
+                convertView = LayoutInflater.from(context).inflate(
+                        R.layout.accessibility_button_chooser_item, parent, /* attachToRoot= */
+                        false);
+                holder = new ViewHolder();
+                holder.mIconView = convertView.findViewById(R.id.accessibility_button_target_icon);
+                holder.mLabelView = convertView.findViewById(
+                        R.id.accessibility_button_target_label);
+                holder.mItemContainer = convertView.findViewById(
+                        R.id.accessibility_button_target_item_container);
+                holder.mViewItem = convertView.findViewById(
+                        R.id.accessibility_button_target_view_item);
+                holder.mSwitchItem = convertView.findViewById(
+                        R.id.accessibility_button_target_switch_item);
+                convertView.setTag(holder);
+            } else {
+                holder = (ViewHolder) convertView.getTag();
+            }
 
-            // TODO(b/146815874): Need to get every service status to update UI
-            return root;
+            final AccessibilityButtonTarget target = mButtonTargets.get(position);
+            holder.mIconView.setImageDrawable(target.getDrawable());
+            holder.mLabelView.setText(target.getLabel());
+
+            updateActionItem(context, holder, target);
+
+            return convertView;
+        }
+
+        private void updateActionItem(@NonNull Context context,
+                @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
+
+            switch (target.getFragmentType()) {
+                case AccessibilityServiceFragmentType.LEGACY:
+                case AccessibilityServiceFragmentType.INVISIBLE:
+                    updateLegacyOrInvisibleActionItemVisibility(context, holder);
+                    break;
+                case AccessibilityServiceFragmentType.INTUITIVE:
+                    updateIntuitiveActionItemVisibility(context, holder, target);
+                    break;
+                case AccessibilityServiceFragmentType.BOUNCE:
+                    updateBounceActionItemVisibility(context, holder);
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected fragment type");
+            }
+        }
+
+        private void updateLegacyOrInvisibleActionItemVisibility(@NonNull Context context,
+                @NonNull ViewHolder holder) {
+            final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+
+            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mViewItem.setVisibility(View.VISIBLE);
+            holder.mSwitchItem.setVisibility(View.GONE);
+            holder.mItemContainer.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+        }
+
+        private void updateIntuitiveActionItemVisibility(@NonNull Context context,
+                @NonNull ViewHolder holder, AccessibilityButtonTarget target) {
+            final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+
+            holder.mViewItem.setImageDrawable(context.getDrawable(R.drawable.ic_delete_item));
+            holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+            holder.mSwitchItem.setVisibility(isEditMenuMode ? View.GONE : View.VISIBLE);
+            holder.mSwitchItem.setChecked(!isEditMenuMode && isServiceEnabled(context, target));
+            holder.mItemContainer.setVisibility(View.VISIBLE);
+        }
+
+        private void updateBounceActionItemVisibility(@NonNull Context context,
+                @NonNull ViewHolder holder) {
+            final boolean isEditMenuMode = mShortcutMenuMode == ShortcutMenuMode.EDIT;
+
+            holder.mViewItem.setImageDrawable(
+                    isEditMenuMode ? context.getDrawable(R.drawable.ic_delete_item)
+                            : context.getDrawable(R.drawable.ic_open_in_new));
+            holder.mViewItem.setVisibility(isEditMenuMode ? View.VISIBLE : View.GONE);
+            holder.mSwitchItem.setVisibility(View.GONE);
+            holder.mItemContainer.setVisibility(View.VISIBLE);
         }
     }
 
     private static class AccessibilityButtonTarget {
-        public String mId;
-        public CharSequence mLabel;
-        public Drawable mDrawable;
-        // TODO(b/146815874): Will add fragment type and related functions
-        public AccessibilityButtonTarget(@NonNull Context context,
+        private String mId;
+        private CharSequence mLabel;
+        private Drawable mDrawable;
+        @AccessibilityServiceFragmentType
+        private int mFragmentType;
+
+        AccessibilityButtonTarget(@NonNull Context context,
                 @NonNull AccessibilityServiceInfo serviceInfo) {
             this.mId = serviceInfo.getComponentName().flattenToString();
             this.mLabel = serviceInfo.getResolveInfo().loadLabel(context.getPackageManager());
             this.mDrawable = serviceInfo.getResolveInfo().loadIcon(context.getPackageManager());
+            this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo);
         }
 
-        public AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
-                int iconRes) {
+        AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
+                int iconRes, @AccessibilityServiceFragmentType int fragmentType) {
             this.mId = id;
             this.mLabel = context.getText(labelResId);
             this.mDrawable = context.getDrawable(iconRes);
+            this.mFragmentType = fragmentType;
         }
 
         public String getId() {
@@ -178,5 +351,105 @@
         public Drawable getDrawable() {
             return mDrawable;
         }
+
+        public int getFragmentType() {
+            return mFragmentType;
+        }
+    }
+
+    private static boolean isServiceEnabled(@NonNull Context context,
+            AccessibilityButtonTarget target) {
+        final AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+        final List<AccessibilityServiceInfo> enabledServices =
+                ams.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+
+        for (AccessibilityServiceInfo info : enabledServices) {
+            final String id = info.getComponentName().flattenToString();
+            if (id.equals(target.getId())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
+        Settings.Secure.putString(getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                mTargets.get(position).getId());
+        // TODO(b/146969684): notify accessibility button clicked.
+        mAlertDialog.dismiss();
+    }
+
+    private void onTargetDeleted(AdapterView<?> parent, View view, int position, long id) {
+        // TODO(b/147027236): Will discuss with UX designer what UX behavior about deleting item
+        //  is good for user.
+        mReadyToBeDisabledTargets.add(mTargets.get(position));
+        mTargets.remove(position);
+        mTargetAdapter.notifyDataSetChanged();
+    }
+
+    private void onCancelButtonClicked() {
+        resetAndUpdateTargets();
+
+        mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
+        mTargetAdapter.notifyDataSetChanged();
+
+        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                getString(R.string.edit_accessibility_shortcut_menu_button));
+
+        updateDialogListeners();
+    }
+
+    private void onEditButtonClicked() {
+        mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT);
+        mTargetAdapter.notifyDataSetChanged();
+
+        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setText(
+                getString(R.string.cancel_accessibility_shortcut_menu_button));
+        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.VISIBLE);
+        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                getString(R.string.save_accessibility_shortcut_menu_button));
+
+        updateDialogListeners();
+    }
+
+    private void onSaveButtonClicked() {
+        disableTargets();
+        resetAndUpdateTargets();
+
+        mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
+        mTargetAdapter.notifyDataSetChanged();
+
+        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
+                getString(R.string.edit_accessibility_shortcut_menu_button));
+
+        updateDialogListeners();
+    }
+
+    private void updateDialogListeners() {
+        final boolean isEditMenuMode =
+                mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT;
+
+        mAlertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(
+                view -> onCancelButtonClicked());
+        mAlertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
+                isEditMenuMode ? view -> onSaveButtonClicked() : view -> onEditButtonClicked());
+        mAlertDialog.getListView().setOnItemClickListener(
+                isEditMenuMode ? this::onTargetDeleted : this::onTargetSelected);
+    }
+
+    private void disableTargets() {
+        for (AccessibilityButtonTarget service : mReadyToBeDisabledTargets) {
+            // TODO(b/146967898): disable services.
+        }
+    }
+
+    private void resetAndUpdateTargets() {
+        mTargets.clear();
+        mTargets.addAll(getServiceTargets(this, mShortcutType));
     }
 }
diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java
index 7307de5..cfbb273 100644
--- a/core/java/com/android/internal/app/AlertActivity.java
+++ b/core/java/com/android/internal/app/AlertActivity.java
@@ -16,9 +16,9 @@
 
 package com.android.internal.app;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.DialogInterface;
 import android.os.Bundle;
 import android.view.KeyEvent;
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 0fd05c1..553721d7 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -18,14 +18,11 @@
 
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
-import com.android.internal.R;
-
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
@@ -33,7 +30,6 @@
 import android.os.Message;
 import android.text.Layout;
 import android.text.TextUtils;
-import android.text.method.LinkMovementMethod;
 import android.text.method.MovementMethod;
 import android.util.AttributeSet;
 import android.util.TypedValue;
@@ -46,7 +42,6 @@
 import android.view.ViewParent;
 import android.view.ViewStub;
 import android.view.Window;
-import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -63,6 +58,8 @@
 import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
 
+import com.android.internal.R;
+
 import java.lang.ref.WeakReference;
 
 public class AlertController {
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index f848309..22a2564 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,7 +17,7 @@
 package com.android.internal.app;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a86b566..b6b548c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -26,7 +26,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.prediction.AppPredictionContext;
@@ -34,6 +33,7 @@
 import android.app.prediction.AppPredictor;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipboardManager;
 import android.content.ComponentName;
@@ -98,10 +98,10 @@
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.view.WindowInsets;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
+import android.widget.Space;
 import android.widget.TextView;
 import android.widget.Toast;
 
@@ -1546,6 +1546,21 @@
         return -1;
     }
 
+    @Override
+    protected boolean shouldAddFooterView() {
+        // To accommodate for window insets
+        return true;
+    }
+
+    @Override
+    protected void applyFooterView(int height) {
+        int count = mChooserMultiProfilePagerAdapter.getItemCount();
+
+        for (int i = 0; i < count; i++) {
+            mChooserMultiProfilePagerAdapter.getAdapterForIndex(i).setFooterHeight(height);
+        }
+    }
+
     void queryTargetServices(ChooserListAdapter adapter) {
         mQueriedTargetServicesTimeMs = System.currentTimeMillis();
 
@@ -2413,12 +2428,12 @@
     }
 
     /**
-     * Intentionally override the {@link ResolverActivity} implementation as we only need that
-     * implementation for the intent resolver case.
+     * Add a footer to the list, to support scrolling behavior below the navbar.
      */
-    @Override
-    protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
-        return insets.consumeSystemWindowInsets();
+    final class FooterViewHolder extends RecyclerView.ViewHolder {
+        FooterViewHolder(View itemView) {
+            super(itemView);
+        }
     }
 
     /**
@@ -2451,11 +2466,14 @@
 
         private boolean mLayoutRequested = false;
 
+        private int mFooterHeight = 0;
+
         private static final int VIEW_TYPE_DIRECT_SHARE = 0;
         private static final int VIEW_TYPE_NORMAL = 1;
         private static final int VIEW_TYPE_PROFILE = 2;
         private static final int VIEW_TYPE_AZ_LABEL = 3;
         private static final int VIEW_TYPE_CALLER_AND_RANK = 4;
+        private static final int VIEW_TYPE_FOOTER = 5;
 
         private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
         private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
@@ -2484,6 +2502,10 @@
             });
         }
 
+        public void setFooterHeight(int height) {
+            mFooterHeight = height;
+        }
+
         /**
          * Calculate the chooser target width to maximize space per item
          *
@@ -2534,6 +2556,10 @@
             return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
         }
 
+        public int getFooterRowCount() {
+            return 1;
+        }
+
         public int getCallerAndRankedTargetRowCount() {
             return (int) Math.ceil(
                     ((float) mChooserListAdapter.getCallerTargetCount()
@@ -2563,6 +2589,7 @@
                             + getCallerAndRankedTargetRowCount()
                             + getAzLabelRowCount()
                             + mChooserListAdapter.getAlphaTargetCount()
+                            + getFooterRowCount()
             );
         }
 
@@ -2578,6 +2605,11 @@
                 case VIEW_TYPE_DIRECT_SHARE:
                 case VIEW_TYPE_CALLER_AND_RANK:
                     return createItemGroupViewHolder(viewType, parent);
+                case VIEW_TYPE_FOOTER:
+                    Space sp = new Space(parent.getContext());
+                    sp.setLayoutParams(new RecyclerView.LayoutParams(
+                            LayoutParams.MATCH_PARENT, mFooterHeight));
+                    return new FooterViewHolder(sp);
                 default:
                     // Since we catch all possible viewTypes above, no chance this is being called.
                     return null;
@@ -2615,6 +2647,8 @@
             countSum += (count = getAzLabelRowCount());
             if (count > 0 && position < countSum) return VIEW_TYPE_AZ_LABEL;
 
+            if (position == getItemCount() - 1) return VIEW_TYPE_FOOTER;
+
             return VIEW_TYPE_NORMAL;
         }
 
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 46025aa..dabaf5a 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -31,9 +31,11 @@
     // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
     // and not be reordered
     int checkOperation(int code, int uid, String packageName);
-    int noteOperation(int code, int uid, String packageName, @nullable String featureId);
+    int noteOperation(int code, int uid, String packageName, @nullable String featureId,
+            boolean shouldCollectAsyncNotedOp, String message);
     int startOperation(IBinder clientId, int code, int uid, String packageName,
-            @nullable String featureId, boolean startIfModeDefault);
+            @nullable String featureId, boolean startIfModeDefault,
+            boolean shouldCollectAsyncNotedOp, String message);
     @UnsupportedAppUsage
     void finishOperation(IBinder clientId, int code, int uid, String packageName,
             @nullable String featureId);
@@ -41,8 +43,6 @@
     void stopWatchingMode(IAppOpsCallback callback);
     int permissionToOpCode(String permission);
     int checkAudioOperation(int code, int usage, int uid, String packageName);
-    void noteAsyncOp(@nullable String callingPackageName, int uid, @nullable String packageName,
-            int opCode, @nullable String featureId, String message);
     boolean shouldCollectNotes(int opCode);
     void setCameraAudioRestriction(int mode);
     // End of methods also called by native code.
@@ -50,7 +50,7 @@
 
     int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
             String proxiedFeatureId, int proxyUid, String proxyPackageName,
-            String proxyFeatureId);
+            String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message);
 
     // Remaining methods are only used in Java.
     int checkPackage(int uid, String packageName);
@@ -58,10 +58,12 @@
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     @UnsupportedAppUsage
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
-    void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis,
-            long endTimeMillis, int flags, in RemoteCallback callback);
-    void getHistoricalOpsFromDiskRaw(int uid, String packageName, in List<String> ops,
-            long beginTimeMillis, long endTimeMillis, int flags, in RemoteCallback callback);
+    void getHistoricalOps(int uid, String packageName, String featureId, in List<String> ops,
+            int filter, long beginTimeMillis, long endTimeMillis, int flags,
+            in RemoteCallback callback);
+    void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+            in List<String> ops, int filter, long beginTimeMillis, long endTimeMillis, int flags,
+            in RemoteCallback callback);
     void offsetHistory(long duration);
     void setHistoryParameters(int mode, long baseSnapshotInterval, int compressionStep);
     void addHistoricalOps(in AppOpsManager.HistoricalOps ops);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index a5f055f..9488e4f 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -20,12 +20,12 @@
 
 import android.annotation.Nullable;
 import android.annotation.StringRes;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
diff --git a/core/java/com/android/internal/app/LocaleHelper.java b/core/java/com/android/internal/app/LocaleHelper.java
index e3d07aa..d418770e 100644
--- a/core/java/com/android/internal/app/LocaleHelper.java
+++ b/core/java/com/android/internal/app/LocaleHelper.java
@@ -17,7 +17,7 @@
 package com.android.internal.app;
 
 import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.icu.text.ListFormatter;
 import android.icu.util.ULocale;
 import android.os.LocaleList;
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index 7517424..3343593f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -16,13 +16,11 @@
 
 package com.android.internal.app;
 
-import com.android.internal.R;
-
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.ListFragment;
 import android.app.backup.BackupManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -38,11 +36,13 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
+import com.android.internal.R;
+
 import java.text.Collator;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
-import java.util.ArrayList;
 
 public class LocalePicker extends ListFragment {
     private static final String TAG = "LocalePicker";
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index 49f77e1..1c5ca59 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.app;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.LocaleList;
 import android.provider.Settings;
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 9a802a9..89aa770 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -16,19 +16,19 @@
 
 package com.android.internal.app;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.AlertDialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.location.LocationManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.widget.Toast;
 import android.util.Log;
-import android.location.LocationManager;
+import android.widget.Toast;
 
 import com.android.internal.R;
 import com.android.internal.location.GpsNetInitiatedHandler;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 8dc3a07..b2417b2 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -24,7 +24,6 @@
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UiThread;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -32,6 +31,7 @@
 import android.app.VoiceInteractor.PickOptionRequest;
 import android.app.VoiceInteractor.PickOptionRequest.Option;
 import android.app.VoiceInteractor.Prompt;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -353,7 +353,11 @@
                     finish();
                 }
             });
-            if (isVoiceInteraction()) {
+
+            boolean hasTouchScreen = getPackageManager()
+                    .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
+
+            if (isVoiceInteraction() || !hasTouchScreen) {
                 rdl.setCollapsed(false);
             }
 
@@ -472,34 +476,52 @@
         finish();
     }
 
+    /**
+     * Numerous layouts are supported, each with optional ViewGroups.
+     * Make sure the inset gets added to the correct View, using
+     * a footer for Lists so it can properly scroll under the navbar.
+     */
+    protected boolean shouldAddFooterView() {
+        if (useLayoutWithDefault()) return true;
+
+        View buttonBar = findViewById(R.id.button_bar);
+        if (buttonBar == null || buttonBar.getVisibility() == View.GONE) return true;
+
+        return false;
+    }
+
+    protected void applyFooterView(int height) {
+        if (mFooterSpacer == null) {
+            mFooterSpacer = new Space(getApplicationContext());
+        } else {
+            ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+                .getCurrentAdapterView().removeFooterView(mFooterSpacer);
+        }
+        mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+                                                                   mSystemWindowInsets.bottom));
+        ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+            .getCurrentAdapterView().addFooterView(mFooterSpacer);
+    }
+
     protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
         mSystemWindowInsets = insets.getSystemWindowInsets();
 
         mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
                 mSystemWindowInsets.right, 0);
 
+        resetButtonBar();
+
         // Need extra padding so the list can fully scroll up
-        if (useLayoutWithDefault()) {
-            if (mFooterSpacer == null) {
-                mFooterSpacer = new Space(getApplicationContext());
-            } else {
-                ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
-                        .getCurrentAdapterView().removeFooterView(mFooterSpacer);
-            }
-            mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
-                                                                       mSystemWindowInsets.bottom));
-            ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
-                    .getCurrentAdapterView().addFooterView(mFooterSpacer);
-        } else {
-            View emptyView = findViewById(R.id.empty);
-            if (emptyView != null) {
-                emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                                     + getResources().getDimensionPixelSize(
-                                             R.dimen.chooser_edge_margin_normal) * 2);
-            }
+        if (shouldAddFooterView()) {
+            applyFooterView(mSystemWindowInsets.bottom);
         }
 
-        resetButtonBar();
+        View emptyView = findViewById(R.id.empty);
+        if (emptyView != null) {
+            emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+                                 + getResources().getDimensionPixelSize(
+                                         R.dimen.chooser_edge_margin_normal) * 2);
+        }
 
         return insets.consumeSystemWindowInsets();
     }
@@ -1283,10 +1305,11 @@
 
         // In case this method is called again (due to activity recreation), avoid adding a new
         // header if one is already present.
-        if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+        if (useHeader && listView.getHeaderViewsCount() == 0) {
             listView.setHeaderDividersEnabled(true);
             listView.addHeaderView(LayoutInflater.from(this).inflate(
-                    R.layout.resolver_different_item_header, listView, false));
+                    R.layout.resolver_different_item_header, listView, false),
+                    null, false);
         }
     }
 
@@ -1349,6 +1372,8 @@
         if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
             setAlwaysButtonEnabled(true, filteredPosition, false);
             mOnceButton.setEnabled(true);
+            // Focus the button if we already have the default option
+            mOnceButton.requestFocus();
             return;
         }
 
@@ -1481,6 +1506,7 @@
                 mOnceButton.setEnabled(hasValidSelection);
                 if (hasValidSelection) {
                     currentAdapterView.smoothScrollToPosition(checkedPos);
+                    mOnceButton.requestFocus();
                 }
                 mLastSelected = checkedPos;
             } else {
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index e71ee66..0cd1202 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -16,28 +16,17 @@
 
 package com.android.internal.app;
 
-import com.android.internal.R;
-import com.android.internal.view.ActionBarPolicy;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuPopupHelper;
-import com.android.internal.view.menu.SubMenuBuilder;
-import com.android.internal.widget.ActionBarContainer;
-import com.android.internal.widget.ActionBarContextView;
-import com.android.internal.widget.ActionBarOverlayLayout;
-import com.android.internal.widget.DecorToolbar;
-import com.android.internal.widget.ScrollingTabContainerView;
-
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.FragmentTransaction;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -58,6 +47,17 @@
 import android.widget.SpinnerAdapter;
 import android.widget.Toolbar;
 
+import com.android.internal.R;
+import com.android.internal.view.ActionBarPolicy;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuPopupHelper;
+import com.android.internal.view.menu.SubMenuBuilder;
+import com.android.internal.widget.ActionBarContainer;
+import com.android.internal.widget.ActionBarContextView;
+import com.android.internal.widget.ActionBarOverlayLayout;
+import com.android.internal.widget.DecorToolbar;
+import com.android.internal.widget.ScrollingTabContainerView;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java
index 6b76a0f..3682b7b 100644
--- a/core/java/com/android/internal/content/PackageMonitor.java
+++ b/core/java/com/android/internal/content/PackageMonitor.java
@@ -16,8 +16,8 @@
 
 package com.android.internal.content;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java
index 6d05f7e..6af03dd 100644
--- a/core/java/com/android/internal/content/ReferrerIntent.java
+++ b/core/java/com/android/internal/content/ReferrerIntent.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.content;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Parcel;
 
diff --git a/core/java/com/android/internal/database/SortCursor.java b/core/java/com/android/internal/database/SortCursor.java
index 7fe809e..230a9b87 100644
--- a/core/java/com/android/internal/database/SortCursor.java
+++ b/core/java/com/android/internal/database/SortCursor.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.database;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.database.AbstractCursor;
 import android.database.Cursor;
 import android.database.DataSetObserver;
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index 857377a..e9d7d05 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -223,7 +223,7 @@
         private final @NonNull ServiceConnection mServiceConnection = this;
         private final @NonNull Runnable mTimeoutDisconnect = this;
 
-        private final @NonNull Context mContext;
+        protected final @NonNull Context mContext;
         private final @NonNull Intent mIntent;
         private final int mBindingFlags;
         private final int mUserId;
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index f916cf6..ed04fd8 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.logging;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.metrics.LogMaker;
 import android.os.Build;
diff --git a/core/java/com/android/internal/net/LegacyVpnInfo.java b/core/java/com/android/internal/net/LegacyVpnInfo.java
index 2ad9759..4eb7ded 100644
--- a/core/java/com/android/internal/net/LegacyVpnInfo.java
+++ b/core/java/com/android/internal/net/LegacyVpnInfo.java
@@ -16,8 +16,8 @@
 
 package com.android.internal.net;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.NetworkInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index e6be549..f5a19fe 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -16,8 +16,8 @@
 
 package com.android.internal.net;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 940cc36..4bb012a 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,10 +16,9 @@
 
 package com.android.internal.net;
 
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.ProxyInfo;
-import android.net.Uri;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
diff --git a/core/java/com/android/internal/os/AndroidPrintStream.java b/core/java/com/android/internal/os/AndroidPrintStream.java
index fe23411..a6e41ff 100644
--- a/core/java/com/android/internal/os/AndroidPrintStream.java
+++ b/core/java/com/android/internal/os/AndroidPrintStream.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Log;
 
 /**
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index 3d027c5..32442f0 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -17,7 +17,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.BasicShellCommandHandler;
 
 import java.io.PrintStream;
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index b1fc369..b3ea118 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.BatteryStats.Uid;
 
 import java.util.List;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index e85508e..b131ab8 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d0a83c4..b3548b8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -21,10 +21,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 4901e1f..95c36ca 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -17,7 +17,7 @@
 package com.android.internal.os;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
diff --git a/core/java/com/android/internal/os/ClassLoaderFactory.java b/core/java/com/android/internal/os/ClassLoaderFactory.java
index d323498..a18943c 100644
--- a/core/java/com/android/internal/os/ClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/ClassLoaderFactory.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Trace;
 
 import dalvik.system.DelegateLastClassLoader;
diff --git a/core/java/com/android/internal/os/FuseAppLoop.java b/core/java/com/android/internal/os/FuseAppLoop.java
index a22615b..ab0cc30 100644
--- a/core/java/com/android/internal/os/FuseAppLoop.java
+++ b/core/java/com/android/internal/os/FuseAppLoop.java
@@ -18,17 +18,19 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.os.ProxyFileDescriptorCallback;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.util.Log;
 import android.util.SparseArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
+
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index c8bfa1b..a11c815 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 0faf962..3db8f4e 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -17,7 +17,7 @@
 package com.android.internal.os;
 
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index dcf8d28..5e3c5df 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -16,9 +16,15 @@
 
 package com.android.internal.os;
 
-import static android.os.Process.*;
+import static android.os.Process.PROC_COMBINE;
+import static android.os.Process.PROC_OUT_FLOAT;
+import static android.os.Process.PROC_OUT_LONG;
+import static android.os.Process.PROC_OUT_STRING;
+import static android.os.Process.PROC_PARENS;
+import static android.os.Process.PROC_SPACE_TERM;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.CpuUsageProto;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -26,10 +32,12 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.text.SimpleDateFormat;
@@ -735,6 +743,63 @@
         return mWorkingProcs.get(index);
     }
 
+    /** Dump cpuinfo in protobuf format. */
+    public final void dumpProto(FileDescriptor fd) {
+        final long now = SystemClock.uptimeMillis();
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        final long currentLoadToken = proto.start(CpuUsageProto.CURRENT_LOAD);
+        proto.write(CpuUsageProto.Load.LOAD1, mLoad1);
+        proto.write(CpuUsageProto.Load.LOAD5, mLoad5);
+        proto.write(CpuUsageProto.Load.LOAD15, mLoad15);
+        proto.end(currentLoadToken);
+
+        proto.write(CpuUsageProto.NOW, now);
+        proto.write(CpuUsageProto.LAST_SAMPLE_TIME, mLastSampleTime);
+        proto.write(CpuUsageProto.CURRENT_SAMPLE_TIME, mCurrentSampleTime);
+        proto.write(CpuUsageProto.LAST_SAMPLE_REAL_TIME, mLastSampleRealTime);
+        proto.write(CpuUsageProto.CURRENT_SAMPLE_REAL_TIME, mCurrentSampleRealTime);
+        proto.write(CpuUsageProto.LAST_SAMPLE_WALL_TIME, mLastSampleWallTime);
+        proto.write(CpuUsageProto.CURRENT_SAMPLE_WALL_TIME, mCurrentSampleWallTime);
+
+        proto.write(CpuUsageProto.TOTAL_USER_TIME, mRelUserTime);
+        proto.write(CpuUsageProto.TOTAL_SYSTEM_TIME, mRelSystemTime);
+        proto.write(CpuUsageProto.TOTAL_IOWAIT_TIME, mRelIoWaitTime);
+        proto.write(CpuUsageProto.TOTAL_IRQ_TIME, mRelIrqTime);
+        proto.write(CpuUsageProto.TOTAL_SOFT_IRQ_TIME, mRelSoftIrqTime);
+        proto.write(CpuUsageProto.TOTAL_IDLE_TIME, mRelIdleTime);
+        final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
+                + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
+        proto.write(CpuUsageProto.TOTAL_TIME, totalTime);
+
+        for (Stats st : mWorkingProcs) {
+            dumpProcessCpuProto(proto, st, null);
+            if (!st.removed && st.workingThreads != null) {
+                for (Stats tst : st.workingThreads) {
+                    dumpProcessCpuProto(proto, tst, st);
+                }
+            }
+        }
+        proto.flush();
+    }
+
+    private static void dumpProcessCpuProto(ProtoOutputStream proto, Stats st, Stats proc) {
+        long statToken = proto.start(CpuUsageProto.PROCESSES);
+        proto.write(CpuUsageProto.Stat.UID, st.uid);
+        proto.write(CpuUsageProto.Stat.PID, st.pid);
+        proto.write(CpuUsageProto.Stat.NAME, st.name);
+        proto.write(CpuUsageProto.Stat.ADDED, st.added);
+        proto.write(CpuUsageProto.Stat.REMOVED, st.removed);
+        proto.write(CpuUsageProto.Stat.UPTIME, st.rel_uptime);
+        proto.write(CpuUsageProto.Stat.USER_TIME, st.rel_utime);
+        proto.write(CpuUsageProto.Stat.SYSTEM_TIME, st.rel_stime);
+        proto.write(CpuUsageProto.Stat.MINOR_FAULTS, st.rel_minfaults);
+        proto.write(CpuUsageProto.Stat.MAJOR_FAULTS, st.rel_majfaults);
+        if (proc != null) {
+            proto.write(CpuUsageProto.Stat.PARENT_PID, proc.pid);
+        }
+        proto.end(statToken);
+    }
+
     final public String printCurrentLoad() {
         StringWriter sw = new StringWriter();
         PrintWriter pw = new FastPrintWriter(sw, false, 128);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index fa823c4..179828c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -16,10 +16,10 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ApplicationErrorReport;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.type.DefaultMimeMapFactory;
 import android.os.Build;
 import android.os.DeadObjectException;
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index d78bfac..003f610 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Helper class for passing more arguments though a message
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 9c6a288..c91c661 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -23,7 +23,7 @@
 import static com.android.internal.os.ZygoteConnectionConstants.CONNECTION_TIMEOUT_MILLIS;
 import static com.android.internal.os.ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ApplicationInfo;
 import android.net.Credentials;
 import android.net.LocalSocket;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 49b4cf8..7b6262b 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -19,8 +19,8 @@
 import static android.system.OsConstants.S_IRWXG;
 import static android.system.OsConstants.S_IRWXO;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.ApplicationLoaders;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.SharedLibraryInfo;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
diff --git a/core/java/com/android/internal/os/ZygoteSecurityException.java b/core/java/com/android/internal/os/ZygoteSecurityException.java
index 7e50cb8..8111483 100644
--- a/core/java/com/android/internal/os/ZygoteSecurityException.java
+++ b/core/java/com/android/internal/os/ZygoteSecurityException.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.os;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Exception thrown when a security policy is violated.
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 4a21d37..8d281b7 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -184,9 +184,8 @@
                             Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
         }
 
-        fetchUsapPoolPolicyProps();
-
         mUsapPoolSupported = true;
+        fetchUsapPoolPolicyProps();
     }
 
     void setForkChild() {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 29b148c..0170978 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -48,8 +48,8 @@
 import android.animation.ObjectAnimator;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.WindowConfiguration;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
diff --git a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
index 791c2d7..f07f667 100644
--- a/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
+++ b/core/java/com/android/internal/policy/PhoneFallbackEventHandler.java
@@ -16,9 +16,9 @@
 
 package com.android.internal.policy;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.KeyguardManager;
 import android.app.SearchManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -33,7 +33,6 @@
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.View;
-import com.android.internal.policy.PhoneWindow;
 
 /**
  * @hide
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 9aa56f0..88a9cb0 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -32,10 +32,10 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.KeyguardManager;
 import android.app.SearchManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
diff --git a/core/java/com/android/internal/preference/YesNoPreference.java b/core/java/com/android/internal/preference/YesNoPreference.java
index 46d14a1..f878054 100644
--- a/core/java/com/android/internal/preference/YesNoPreference.java
+++ b/core/java/com/android/internal/preference/YesNoPreference.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.preference;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Parcel;
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 6c7e3dc..6fd271c 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -62,4 +62,6 @@
     void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
     void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
     void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
+    void onRegistrationFailed(in CellIdentity cellIdentity,
+             String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
 }
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 4e40503..8e97ae1 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -63,7 +63,7 @@
     void notifyDataActivity(int state);
     void notifyDataActivityForSubscriber(in int subId, int state);
     void notifyDataConnectionForSubscriber(
-            int phoneId, int subId, String apnType, in PreciseDataConnectionState preciseState);
+            int phoneId, int subId, int apnType, in PreciseDataConnectionState preciseState);
     @UnsupportedAppUsage
     void notifyDataConnectionFailed(String apnType);
     // Uses CellIdentity which is Parcelable here; will convert to CellLocation in client.
@@ -75,7 +75,7 @@
             int foregroundCallState, int backgroundCallState);
     void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
             int preciseDisconnectCause);
-    void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType, String apn,
+    void notifyPreciseDataConnectionFailed(int phoneId, int subId, int apnType, String apn,
             int failCause);
     void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
     void notifySrvccStateChanged(in int subId, in int lteState);
@@ -97,4 +97,6 @@
     void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int subId,
             int callNetworkType);
     void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo);
+    void notifyRegistrationFailed(int slotIndex, int subId, in CellIdentity cellIdentity,
+            String chosenPlmn, int domain, int causeCode, int additionalCauseCode);
 }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index c7ec2cd..cd69a02 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArraySet;
 
 import dalvik.system.VMRuntime;
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 121ae1a..7e3c171 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/com/android/internal/util/BitwiseInputStream.java b/core/java/com/android/internal/util/BitwiseInputStream.java
index 6ff67e9..ffae3ce 100644
--- a/core/java/com/android/internal/util/BitwiseInputStream.java
+++ b/core/java/com/android/internal/util/BitwiseInputStream.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * An object that provides bitwise incremental read access to a byte array.
diff --git a/core/java/com/android/internal/util/BitwiseOutputStream.java b/core/java/com/android/internal/util/BitwiseOutputStream.java
index cdd6f17..9f41508 100644
--- a/core/java/com/android/internal/util/BitwiseOutputStream.java
+++ b/core/java/com/android/internal/util/BitwiseOutputStream.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * An object that provides bitwise incremental write access to a byte array.
diff --git a/core/java/com/android/internal/util/CharSequences.java b/core/java/com/android/internal/util/CharSequences.java
index 6b6c43c..82ef200 100644
--- a/core/java/com/android/internal/util/CharSequences.java
+++ b/core/java/com/android/internal/util/CharSequences.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * {@link CharSequence} utility methods.
diff --git a/core/java/com/android/internal/util/FastMath.java b/core/java/com/android/internal/util/FastMath.java
index 35efe70..b7dbee5 100644
--- a/core/java/com/android/internal/util/FastMath.java
+++ b/core/java/com/android/internal/util/FastMath.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Fast and loose math routines.
diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java
index 981fbaa..63124de 100644
--- a/core/java/com/android/internal/util/FastPrintWriter.java
+++ b/core/java/com/android/internal/util/FastPrintWriter.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.Log;
 import android.util.Printer;
 
diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java
index 9f76aeb..94e07a8 100644
--- a/core/java/com/android/internal/util/FastXmlSerializer.java
+++ b/core/java/com/android/internal/util/FastXmlSerializer.java
@@ -16,9 +16,10 @@
 
 package com.android.internal.util;
 
+import android.compat.annotation.UnsupportedAppUsage;
+
 import org.xmlpull.v1.XmlSerializer;
 
-import android.annotation.UnsupportedAppUsage;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
diff --git a/core/java/com/android/internal/util/GrowingArrayUtils.java b/core/java/com/android/internal/util/GrowingArrayUtils.java
index 9f56366..597fe6b 100644
--- a/core/java/com/android/internal/util/GrowingArrayUtils.java
+++ b/core/java/com/android/internal/util/GrowingArrayUtils.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A helper class that aims to provide comparable growth performance to ArrayList, but on primitive
diff --git a/core/java/com/android/internal/util/HexDump.java b/core/java/com/android/internal/util/HexDump.java
index 6ffc928..ad88dd6 100644
--- a/core/java/com/android/internal/util/HexDump.java
+++ b/core/java/com/android/internal/util/HexDump.java
@@ -17,7 +17,7 @@
 package com.android.internal.util;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 public class HexDump
 {
diff --git a/core/java/com/android/internal/util/IState.java b/core/java/com/android/internal/util/IState.java
index eb66e2c..07837bf 100644
--- a/core/java/com/android/internal/util/IState.java
+++ b/core/java/com/android/internal/util/IState.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Message;
 
 /**
diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java
index 03a555e..34c6a05 100644
--- a/core/java/com/android/internal/util/IndentingPrintWriter.java
+++ b/core/java/com/android/internal/util/IndentingPrintWriter.java
@@ -16,7 +16,8 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.io.PrintWriter;
 import java.io.Writer;
 import java.util.Arrays;
diff --git a/core/java/com/android/internal/util/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java
index 065cc5b2..a9d8f72 100644
--- a/core/java/com/android/internal/util/JournaledFile.java
+++ b/core/java/com/android/internal/util/JournaledFile.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 
 import java.io.File;
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 27c2478..67cfc3a 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -121,7 +121,11 @@
     }
 
     public static boolean isEnabled(Context ctx) {
-        return Build.IS_DEBUGGABLE && getInstance(ctx).mEnabled;
+        return getInstance(ctx).isEnabled();
+    }
+
+    public boolean isEnabled() {
+        return Build.IS_DEBUGGABLE && mEnabled;
     }
 
     /**
diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java
index 362bc92..5de77d9 100644
--- a/core/java/com/android/internal/util/MemInfoReader.java
+++ b/core/java/com/android/internal/util/MemInfoReader.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Debug;
 import android.os.StrictMode;
 
@@ -107,9 +107,12 @@
      * Amount of RAM that is in use by the kernel for actual allocations.
      */
     public long getKernelUsedSizeKb() {
-        return mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]
-                + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES]
-                + mInfos[Debug.MEMINFO_KERNEL_STACK];
+        long size = mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]
+                + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES];
+        if (!Debug.isVmapStack()) {
+            size += mInfos[Debug.MEMINFO_KERNEL_STACK];
+        }
+        return size;
     }
 
     public long getSwapTotalSizeKb() {
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 3fff5c2..5bc96d8 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -18,7 +18,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.text.TextUtils;
 
 import java.util.Collection;
diff --git a/core/java/com/android/internal/util/State.java b/core/java/com/android/internal/util/State.java
index 3c61e03..636378e 100644
--- a/core/java/com/android/internal/util/State.java
+++ b/core/java/com/android/internal/util/State.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Message;
 
 /**
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 6c217e5..0c24065 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 8799e3d..c1be33a 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -16,10 +16,10 @@
 
 package com.android.internal.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.text.TextUtils;
 import android.util.ArrayMap;
diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java
index d18c35e..d16cb43 100644
--- a/core/java/com/android/internal/view/ActionBarPolicy.java
+++ b/core/java/com/android/internal/view/ActionBarPolicy.java
@@ -16,15 +16,15 @@
 
 package com.android.internal.view;
 
-import com.android.internal.R;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
 
+import com.android.internal.R;
+
 /**
  * Allows components to query for various configuration policy decisions
  * about how the action bar should lay out and behave on the current device.
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 2ac2975..5dd3389b 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.view;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
@@ -34,8 +35,6 @@
 
 import com.android.internal.os.IResultReceiver;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.io.IOException;
 
 public class BaseIWindow extends IWindow.Stub {
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index ececba1..6278d4a3 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 1b133d2..a5964b5 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -20,7 +20,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.ServiceConnection;
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index 0c057ea..a41048c 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -19,7 +19,7 @@
 import android.annotation.AnyThread;
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.inputmethodservice.AbstractInputMethodService;
 import android.os.Bundle;
 import android.os.Handler;
diff --git a/core/java/com/android/internal/view/WindowManagerPolicyThread.java b/core/java/com/android/internal/view/WindowManagerPolicyThread.java
index b009a2d..6d691fc 100644
--- a/core/java/com/android/internal/view/WindowManagerPolicyThread.java
+++ b/core/java/com/android/internal/view/WindowManagerPolicyThread.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.view;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Looper;
 
 /**
diff --git a/core/java/com/android/internal/view/menu/ActionMenu.java b/core/java/com/android/internal/view/menu/ActionMenu.java
index 977c1f6..6482629 100644
--- a/core/java/com/android/internal/view/menu/ActionMenu.java
+++ b/core/java/com/android/internal/view/menu/ActionMenu.java
@@ -16,10 +16,7 @@
 
 package com.android.internal.view.menu;
 
-import java.util.ArrayList;
-import java.util.List;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -30,6 +27,9 @@
 import android.view.MenuItem;
 import android.view.SubMenu;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * @hide
  */
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItem.java b/core/java/com/android/internal/view/menu/ActionMenuItem.java
index ed253d5..bd8bcb9 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItem.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItem.java
@@ -17,7 +17,7 @@
 package com.android.internal.view.menu;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.ColorStateList;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index eb94db3..7622b93 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.view.menu;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
diff --git a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
index 3d3aceb..a9f5e47 100644
--- a/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/ContextMenuBuilder.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.view.menu;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.IBinder;
diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java
index 3d888d3..539c71e 100644
--- a/core/java/com/android/internal/view/menu/IconMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java
@@ -16,13 +16,12 @@
 
 package com.android.internal.view.menu;
 
-import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.text.Layout;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.Gravity;
@@ -30,7 +29,8 @@
 import android.view.View;
 import android.view.ViewDebug;
 import android.widget.TextView;
-import android.text.Layout;
+
+import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
 
 /**
  * The item view for each item in the {@link IconMenuView}.
diff --git a/core/java/com/android/internal/view/menu/IconMenuView.java b/core/java/com/android/internal/view/menu/IconMenuView.java
index 6f26434..9e240db 100644
--- a/core/java/com/android/internal/view/menu/IconMenuView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuView.java
@@ -16,9 +16,7 @@
 
 package com.android.internal.view.menu;
 
-import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -29,10 +27,12 @@
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
-import android.view.LayoutInflater;
+
+import com.android.internal.view.menu.MenuBuilder.ItemInvoker;
 
 import java.util.ArrayList;
 
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 0e07ca7..b31ae38 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -18,7 +18,7 @@
 
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
diff --git a/core/java/com/android/internal/view/menu/MenuDialogHelper.java b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
index 88d0a03..d02b8f6 100644
--- a/core/java/com/android/internal/view/menu/MenuDialogHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuDialogHelper.java
@@ -16,9 +16,9 @@
 
 package com.android.internal.view.menu;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.DialogInterface;
 import android.os.IBinder;
 import android.view.KeyEvent;
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 994a9c1..218f518 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -16,10 +16,8 @@
 
 package com.android.internal.view.menu;
 
-import com.android.internal.view.menu.MenuView.ItemView;
-
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -39,6 +37,8 @@
 import android.view.ViewDebug;
 import android.widget.LinearLayout;
 
+import com.android.internal.view.menu.MenuView.ItemView;
+
 /**
  * @hide
  */
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index d00108e..bac6025 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StyleRes;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index c5df8ad..35b8fef 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.Parcelable;
 import android.view.ViewGroup;
diff --git a/core/java/com/android/internal/view/menu/MenuView.java b/core/java/com/android/internal/view/menu/MenuView.java
index 67a5530..a31c820 100644
--- a/core/java/com/android/internal/view/menu/MenuView.java
+++ b/core/java/com/android/internal/view/menu/MenuView.java
@@ -16,10 +16,7 @@
 
 package com.android.internal.view.menu;
 
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.view.menu.MenuItemImpl;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.drawable.Drawable;
 
 /**
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index cf6d974..6eb215e 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.view.menu;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.view.Menu;
diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java
index 9ccee7f..0f0c1a3 100644
--- a/core/java/com/android/internal/widget/AbsActionBarView.java
+++ b/core/java/com/android/internal/widget/AbsActionBarView.java
@@ -15,26 +15,25 @@
  */
 package com.android.internal.widget;
 
-import com.android.internal.R;
-
-import android.util.TypedValue;
-import android.view.ContextThemeWrapper;
-import android.view.MotionEvent;
-import android.widget.ActionMenuPresenter;
-import android.widget.ActionMenuView;
-
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.DecelerateInterpolator;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
+
+import com.android.internal.R;
 
 public abstract class AbsActionBarView extends ViewGroup {
     private static final TimeInterpolator sAlphaInterpolator = new DecelerateInterpolator();
diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java
index 78ed53f..051526e 100644
--- a/core/java/com/android/internal/widget/ActionBarContextView.java
+++ b/core/java/com/android/internal/widget/ActionBarContextView.java
@@ -15,13 +15,7 @@
  */
 package com.android.internal.widget;
 
-import com.android.internal.R;
-
-import android.widget.ActionMenuPresenter;
-import android.widget.ActionMenuView;
-import com.android.internal.view.menu.MenuBuilder;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
@@ -32,9 +26,14 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
+import android.widget.ActionMenuPresenter;
+import android.widget.ActionMenuView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
+
 /**
  * @hide
  */
diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
index e9e3cda..aca0b71 100644
--- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
+++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java
@@ -18,7 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -41,6 +41,7 @@
 import android.view.WindowInsets;
 import android.widget.OverScroller;
 import android.widget.Toolbar;
+
 import com.android.internal.view.menu.MenuPresenter;
 
 /**
diff --git a/core/java/com/android/internal/widget/AlertDialogLayout.java b/core/java/com/android/internal/widget/AlertDialogLayout.java
index 7a01749..d879b6d 100644
--- a/core/java/com/android/internal/widget/AlertDialogLayout.java
+++ b/core/java/com/android/internal/widget/AlertDialogLayout.java
@@ -18,8 +18,8 @@
 
 import android.annotation.AttrRes;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.StyleRes;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
diff --git a/core/java/com/android/internal/widget/ButtonBarLayout.java b/core/java/com/android/internal/widget/ButtonBarLayout.java
index 0ca6743..ff13107 100644
--- a/core/java/com/android/internal/widget/ButtonBarLayout.java
+++ b/core/java/com/android/internal/widget/ButtonBarLayout.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
diff --git a/core/java/com/android/internal/widget/CachingIconView.java b/core/java/com/android/internal/widget/CachingIconView.java
index 35bff6d..74ad815 100644
--- a/core/java/com/android/internal/widget/CachingIconView.java
+++ b/core/java/com/android/internal/widget/CachingIconView.java
@@ -18,7 +18,7 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
diff --git a/core/java/com/android/internal/widget/DialogTitle.java b/core/java/com/android/internal/widget/DialogTitle.java
index 405436c..0bfd684 100644
--- a/core/java/com/android/internal/widget/DialogTitle.java
+++ b/core/java/com/android/internal/widget/DialogTitle.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.text.Layout;
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 2b648e9..ff3543c8 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.text.Editable;
 import android.text.method.KeyListener;
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 13cc98b..de02eb0 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -88,4 +88,5 @@
             in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
+    boolean hasSecureLockScreen();
 }
diff --git a/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java b/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java
index cc7911d..9ef9f69 100644
--- a/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java
+++ b/core/java/com/android/internal/widget/LinearLayoutWithDefaultTouchRecepient.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.View;
 import android.view.MotionEvent;
+import android.view.View;
 import android.widget.LinearLayout;
 
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index cff39f1..f37a468 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -25,15 +25,14 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.PasswordMetrics;
 import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -56,10 +55,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 
-import com.google.android.collect.Lists;
-
 import libcore.util.HexEncoding;
 
+import com.google.android.collect.Lists;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.security.MessageDigest;
@@ -88,7 +87,6 @@
      */
     public static final long FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS = 1000L;
 
-
     /**
      * This dictates when we start telling the user that continued failed attempts will wipe
      * their device.
@@ -1601,6 +1599,11 @@
         public static final int STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN = 0x20;
 
         /**
+         * Strong authentication is required to prepare for unattended upgrade.
+         */
+        public static final int STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE = 0x40;
+
+        /**
          * Strong auth flags that do not prevent biometric methods from being accepted as auth.
          * If any other flags are set, biometric authentication is disabled.
          */
@@ -1735,8 +1738,11 @@
      */
     public boolean hasSecureLockScreen() {
         if (mHasSecureLockScreen == null) {
-            mHasSecureLockScreen = Boolean.valueOf(mContext.getPackageManager()
-                    .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN));
+            try {
+                mHasSecureLockScreen = Boolean.valueOf(getLockSettings().hasSecureLockScreen());
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
+            }
         }
         return mHasSecureLockScreen.booleanValue();
     }
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 74a0aa3..4ddc782 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -19,7 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java
index dd05576..90a18ef 100644
--- a/core/java/com/android/internal/widget/LockSettingsInternal.java
+++ b/core/java/com/android/internal/widget/LockSettingsInternal.java
@@ -77,4 +77,34 @@
      * @return the user password metrics.
      */
     public abstract @Nullable PasswordMetrics getUserPasswordMetrics(int userHandle);
+
+    /**
+     * Prepare for reboot escrow. This triggers the strong auth to be required. After the escrow
+     * is complete as indicated by calling to the listener registered with {@link
+     * #setRebootEscrowListener}, then {@link #armRebootEscrow()} should be called before
+     * rebooting to apply the update.
+     */
+    public abstract void prepareRebootEscrow();
+
+    /**
+     * Registers a listener for when the RebootEscrow HAL has stored its data needed for rebooting
+     * for an OTA.
+     *
+     * @see RebootEscrowListener
+     * @param listener
+     */
+    public abstract void setRebootEscrowListener(RebootEscrowListener listener);
+
+    /**
+     * Requests that any data needed for rebooting is cleared from the RebootEscrow HAL.
+     */
+    public abstract void clearRebootEscrow();
+
+    /**
+     * Should be called immediately before rebooting for an update. This depends on {@link
+     * #prepareRebootEscrow()} having been called and the escrow completing.
+     *
+     * @return true if the arming worked
+     */
+    public abstract boolean armRebootEscrow();
 }
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 9b87dd2..55f30fb 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -308,7 +308,7 @@
 
     @Override
     public int hashCode() {
-        // Effective Java — Item 9
+        // Effective Java — Always override hashCode when you override equals
         return (17 + mType) * 31 + mCredential.hashCode();
     }
 
diff --git a/core/java/com/android/internal/widget/NumericTextView.java b/core/java/com/android/internal/widget/NumericTextView.java
index d215670..c8f9011 100644
--- a/core/java/com/android/internal/widget/NumericTextView.java
+++ b/core/java/com/android/internal/widget/NumericTextView.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 37046af..dc8d57a 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Paint;
diff --git a/core/java/com/android/internal/widget/PreferenceImageView.java b/core/java/com/android/internal/widget/PreferenceImageView.java
index 02a0b8d..43b6b5a 100644
--- a/core/java/com/android/internal/widget/PreferenceImageView.java
+++ b/core/java/com/android/internal/widget/PreferenceImageView.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.widget.ImageView;
diff --git a/core/java/com/android/internal/widget/RebootEscrowListener.java b/core/java/com/android/internal/widget/RebootEscrowListener.java
new file mode 100644
index 0000000..1654532
--- /dev/null
+++ b/core/java/com/android/internal/widget/RebootEscrowListener.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+/**
+ * Private API to be notified about reboot escrow events.
+ *
+ * {@hide}
+ */
+public interface RebootEscrowListener {
+    /**
+     * Called when the preparation status has changed. When {@code prepared} is {@code true} the
+     * user has entered their lock screen knowledge factor (LSKF) and the HAL has confirmed that
+     * it is ready to retrieve the secret after a reboot. When {@code prepared} is {@code false}
+     * then those conditions are not true.
+     */
+    void onPreparedForReboot(boolean prepared);
+}
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index b66a7b4..43a227a 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -20,7 +20,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.database.Observable;
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index 1bb2ba2..e0c3823 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -187,7 +187,7 @@
 
     public void setCollapsed(boolean collapsed) {
         if (!isLaidOut()) {
-            mOpenOnLayout = collapsed;
+            mOpenOnLayout = !collapsed;
         } else {
             smoothScrollTo(collapsed ? mCollapsibleHeight : 0, 0);
         }
diff --git a/core/java/com/android/internal/widget/ScrollBarUtils.java b/core/java/com/android/internal/widget/ScrollBarUtils.java
index 982e315..3e9d697 100644
--- a/core/java/com/android/internal/widget/ScrollBarUtils.java
+++ b/core/java/com/android/internal/widget/ScrollBarUtils.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 public class ScrollBarUtils {
 
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index 5d48ab9..aa0b0bb 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -15,13 +15,11 @@
  */
 package com.android.internal.widget;
 
-import com.android.internal.view.ActionBarPolicy;
-
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActionBar;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
@@ -42,6 +40,8 @@
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import com.android.internal.view.ActionBarPolicy;
+
 /**
  * This widget implements the dynamic action bar tab behavior that can change
  * across different configurations or circumstances.
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index 4b5d624..5e6f3a4 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -34,12 +34,12 @@
 import android.view.ViewGroup;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
 import android.view.animation.LinearInterpolator;
 import android.view.animation.TranslateAnimation;
-import android.view.animation.Animation.AnimationListener;
 import android.widget.ImageView;
-import android.widget.TextView;
 import android.widget.ImageView.ScaleType;
+import android.widget.TextView;
 
 import com.android.internal.R;
 
diff --git a/core/java/com/android/internal/widget/TextViewInputDisabler.java b/core/java/com/android/internal/widget/TextViewInputDisabler.java
index 8d8f0fe..57806eb 100644
--- a/core/java/com/android/internal/widget/TextViewInputDisabler.java
+++ b/core/java/com/android/internal/widget/TextViewInputDisabler.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.text.InputFilter;
 import android.text.Spanned;
 import android.widget.TextView;
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 7d36b02..c8a86d1 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -18,7 +18,7 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
diff --git a/core/java/com/android/server/ResettableTimeout.java b/core/java/com/android/server/ResettableTimeout.java
index 64083f7..511af94 100644
--- a/core/java/com/android/server/ResettableTimeout.java
+++ b/core/java/com/android/server/ResettableTimeout.java
@@ -16,10 +16,9 @@
 
 package com.android.server;
 
-import android.os.SystemClock;
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.ConditionVariable;
+import android.os.SystemClock;
 
 /**
  * Utility class that you can call on with a timeout, and get called back
diff --git a/core/java/com/android/server/net/BaseNetworkObserver.java b/core/java/com/android/server/net/BaseNetworkObserver.java
index e1a10a5..2a9c0b4 100644
--- a/core/java/com/android/server/net/BaseNetworkObserver.java
+++ b/core/java/com/android/server/net/BaseNetworkObserver.java
@@ -16,7 +16,7 @@
 
 package com.android.server.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.INetworkManagementEventObserver;
 import android.net.LinkAddress;
 import android.net.RouteInfo;
diff --git a/core/java/com/android/server/net/NetlinkTracker.java b/core/java/com/android/server/net/NetlinkTracker.java
index 647fb5b..b57397f 100644
--- a/core/java/com/android/server/net/NetlinkTracker.java
+++ b/core/java/com/android/server/net/NetlinkTracker.java
@@ -16,7 +16,7 @@
 
 package com.android.server.net;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
diff --git a/core/java/com/google/android/collect/Lists.java b/core/java/com/google/android/collect/Lists.java
index 8f6594a..585847d 100644
--- a/core/java/com/google/android/collect/Lists.java
+++ b/core/java/com/google/android/collect/Lists.java
@@ -16,7 +16,8 @@
 
 package com.google.android.collect;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import java.util.ArrayList;
 import java.util.Collections;
 
diff --git a/core/java/com/google/android/collect/Maps.java b/core/java/com/google/android/collect/Maps.java
index 6ba3320..cd4c128 100644
--- a/core/java/com/google/android/collect/Maps.java
+++ b/core/java/com/google/android/collect/Maps.java
@@ -16,7 +16,7 @@
 
 package com.google.android.collect;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArrayMap;
 
 import java.util.HashMap;
diff --git a/core/java/com/google/android/collect/Sets.java b/core/java/com/google/android/collect/Sets.java
index 09b5e51..c67a88a 100644
--- a/core/java/com/google/android/collect/Sets.java
+++ b/core/java/com/google/android/collect/Sets.java
@@ -16,7 +16,7 @@
 
 package com.google.android.collect;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.ArraySet;
 
 import java.util.Collections;
diff --git a/core/java/com/google/android/util/AbstractMessageParser.java b/core/java/com/google/android/util/AbstractMessageParser.java
index f11e6b2..0da7607 100644
--- a/core/java/com/google/android/util/AbstractMessageParser.java
+++ b/core/java/com/google/android/util/AbstractMessageParser.java
@@ -16,7 +16,7 @@
 
 package com.google.android.util;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.ArrayList;
 import java.util.HashMap;
diff --git a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java
index 36d6e22..2848ad7 100644
--- a/core/java/org/apache/http/conn/ssl/AbstractVerifier.java
+++ b/core/java/org/apache/http/conn/ssl/AbstractVerifier.java
@@ -31,7 +31,7 @@
 
 package org.apache.http.conn.ssl;
 
-import java.util.regex.Pattern;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.IOException;
 import java.security.cert.Certificate;
@@ -43,10 +43,10 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
-import java.util.logging.Logger;
 import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
 
-import android.annotation.UnsupportedAppUsage;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.SSLSocket;
diff --git a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
index b2e8b5e..ffae757 100644
--- a/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
+++ b/core/java/org/apache/http/conn/ssl/SSLSocketFactory.java
@@ -31,20 +31,14 @@
 
 package org.apache.http.conn.ssl;
 
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+
 import org.apache.http.conn.scheme.HostNameResolver;
 import org.apache.http.conn.scheme.LayeredSocketFactory;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -57,6 +51,14 @@
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
 
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
 /**
  * Layered socket factory for TLS/SSL connections, based on JSSE.
  *.
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 98ce8b0..6e6746f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -345,8 +345,9 @@
 
     srcs: [
         "android/graphics/apex/android_bitmap.cpp",
-        "android/graphics/apex/android_region.cpp",
+        "android/graphics/apex/android_matrix.cpp",
         "android/graphics/apex/android_paint.cpp",
+        "android/graphics/apex/android_region.cpp",
 
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
diff --git a/core/jni/android/graphics/Matrix.h b/core/jni/android/graphics/Matrix.h
index 11c9e72..fe90d2e 100644
--- a/core/jni/android/graphics/Matrix.h
+++ b/core/jni/android/graphics/Matrix.h
@@ -23,7 +23,7 @@
 namespace android {
 
 /* Gets the underlying SkMatrix from a Matrix object. */
-extern SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
+SkMatrix* android_graphics_Matrix_getSkMatrix(JNIEnv* env, jobject matrixObj);
 
 } // namespace android
 
diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp
index 6095ffa..f5e2a52 100644
--- a/core/jni/android/graphics/Shader.cpp
+++ b/core/jni/android/graphics/Shader.cpp
@@ -5,7 +5,7 @@
 #include "SkShader.h"
 #include "SkBlendMode.h"
 #include "core_jni_helpers.h"
-#include "src/shaders/SkRTShader.h"
+#include "include/effects/SkRuntimeEffect.h"
 
 #include <jni.h>
 
@@ -214,14 +214,14 @@
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
-        jbyteArray inputs, jlong colorSpaceHandle) {
-    SkRuntimeShaderFactory* factory = reinterpret_cast<SkRuntimeShaderFactory*>(shaderFactory);
+        jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
+    SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
     AutoJavaByteArray arInputs(env, inputs);
 
     sk_sp<SkData> fData;
     fData = SkData::MakeWithCopy(arInputs.ptr(), arInputs.length());
     const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
-    sk_sp<SkShader> shader = factory->make(fData, matrix);
+    sk_sp<SkShader> shader = effect->makeShader(fData, nullptr, 0, matrix, isOpaque == JNI_TRUE);
     ThrowIAE_IfNull(env, shader);
 
     return reinterpret_cast<jlong>(shader.release());
@@ -229,24 +229,22 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl,
-        jboolean isOpaque) {
+static jlong RuntimeShader_createShaderFactory(JNIEnv* env, jobject, jstring sksl) {
     ScopedUtfChars strSksl(env, sksl);
-    SkRuntimeShaderFactory* shaderFactory = new SkRuntimeShaderFactory(SkString(strSksl.c_str()),
-            isOpaque == JNI_TRUE);
-    ThrowIAE_IfNull(env, shaderFactory);
+    sk_sp<SkRuntimeEffect> effect = std::get<0>(SkRuntimeEffect::Make(SkString(strSksl.c_str())));
+    ThrowIAE_IfNull(env, effect);
 
-    return reinterpret_cast<jlong>(shaderFactory);
+    return reinterpret_cast<jlong>(effect.release());
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
-static void RuntimeShader_delete(SkRuntimeShaderFactory* shaderFactory) {
-    delete shaderFactory;
+static void Effect_safeUnref(SkRuntimeEffect* effect) {
+    SkSafeUnref(effect);
 }
 
 static jlong RuntimeShader_getNativeFinalizer(JNIEnv*, jobject) {
-    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&RuntimeShader_delete));
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&Effect_safeUnref));
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
@@ -282,8 +280,8 @@
 
 static const JNINativeMethod gRuntimeShaderMethods[] = {
     { "nativeGetFinalizer",   "()J",    (void*)RuntimeShader_getNativeFinalizer },
-    { "nativeCreate",     "(JJ[BJ)J",  (void*)RuntimeShader_create     },
-    { "nativeCreateShaderFactory",     "(Ljava/lang/String;Z)J",
+    { "nativeCreate",     "(JJ[BJZ)J",  (void*)RuntimeShader_create     },
+    { "nativeCreateShaderFactory",     "(Ljava/lang/String;)J",
       (void*)RuntimeShader_createShaderFactory     },
 };
 
diff --git a/core/jni/android/graphics/apex/android_matrix.cpp b/core/jni/android/graphics/apex/android_matrix.cpp
new file mode 100644
index 0000000..309360d
--- /dev/null
+++ b/core/jni/android/graphics/apex/android_matrix.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include "android/graphics/matrix.h"
+#include "Matrix.h"
+
+bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]) {
+    static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMSkewX ==  1, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMSkewY ==  3, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
+
+    SkMatrix* m = android::android_graphics_Matrix_getSkMatrix(env, matrixObj);
+    if (m != nullptr) {
+        m->get9(values);
+        return true;
+    }
+    return false;
+}
diff --git a/core/jni/android/graphics/apex/include/android/graphics/matrix.h b/core/jni/android/graphics/apex/include/android/graphics/matrix.h
new file mode 100644
index 0000000..4039cd1
--- /dev/null
+++ b/core/jni/android/graphics/apex/include/android/graphics/matrix.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_GRAPHICS_MATRIX_H
+#define ANDROID_GRAPHICS_MATRIX_H
+
+#include <jni.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+/**
+ * Returns an array of floats that represents the 3x3 matrix of the java object.
+ * @param values The 9 values of the 3x3 matrix in the following order.
+ *               values[0] = scaleX  values[1] = skewX   values[2] = transX
+ *               values[3] = skewY   values[4] = scaleY  values[5] = transY
+ *               values[6] = persp0  values[7] = persp1  values[8] = persp2
+ * @return true if the values param was populated and false otherwise.
+
+ */
+bool AMatrix_getContents(JNIEnv* env, jobject matrixObj, float values[9]);
+
+__END_DECLS
+
+#endif // ANDROID_GRAPHICS_MATRIX_H
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 2265268..79f62cb 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -59,7 +59,6 @@
     jfieldID hasFocus;
     jfieldID hasWallpaper;
     jfieldID paused;
-    jfieldID layer;
     jfieldID ownerPid;
     jfieldID ownerUid;
     jfieldID inputFeatures;
@@ -152,8 +151,6 @@
             gInputWindowHandleClassInfo.hasWallpaper);
     mInfo.paused = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.paused);
-    mInfo.layer = env->GetIntField(obj,
-            gInputWindowHandleClassInfo.layer);
     mInfo.ownerPid = env->GetIntField(obj,
             gInputWindowHandleClassInfo.ownerPid);
     mInfo.ownerUid = env->GetIntField(obj,
@@ -332,9 +329,6 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.paused, clazz,
             "paused", "Z");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.layer, clazz,
-            "layer", "I");
-
     GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz,
             "ownerPid", "I");
 
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 08aa1d9..ba7fe7f 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -24,9 +24,7 @@
 #include <linux/tcp.h>
 #include <net/if.h>
 #include <netinet/ether.h>
-#include <netinet/icmp6.h>
 #include <netinet/ip.h>
-#include <netinet/ip6.h>
 #include <netinet/udp.h>
 
 #include <android_runtime/AndroidRuntime.h>
@@ -102,98 +100,6 @@
     }
 
 }
-static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
-        jint ifIndex)
-{
-    static const int kLinkLocalHopLimit = 255;
-
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-
-    // Set an ICMPv6 filter that only passes Router Solicitations.
-    struct icmp6_filter rs_only;
-    ICMP6_FILTER_SETBLOCKALL(&rs_only);
-    ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
-    socklen_t len = sizeof(rs_only);
-    if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "setsockopt(ICMP6_FILTER): %s", strerror(errno));
-        return;
-    }
-
-    // Most/all of the rest of these options can be set via Java code, but
-    // because we're here on account of setting an icmp6_filter go ahead
-    // and do it all natively for now.
-    //
-    // TODO: Consider moving these out to Java.
-
-    // Set the multicast hoplimit to 255 (link-local only).
-    int hops = kLinkLocalHopLimit;
-    len = sizeof(hops);
-    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
-        return;
-    }
-
-    // Set the unicast hoplimit to 255 (link-local only).
-    hops = kLinkLocalHopLimit;
-    len = sizeof(hops);
-    if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
-        return;
-    }
-
-    // Explicitly disable multicast loopback.
-    int off = 0;
-    len = sizeof(off);
-    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
-        return;
-    }
-
-    // Specify the IPv6 interface to use for outbound multicast.
-    len = sizeof(ifIndex);
-    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
-        return;
-    }
-
-    // Additional options to be considered:
-    //     - IPV6_TCLASS
-    //     - IPV6_RECVPKTINFO
-    //     - IPV6_RECVHOPLIMIT
-
-    // Bind to [::].
-    const struct sockaddr_in6 sin6 = {
-            .sin6_family = AF_INET6,
-            .sin6_port = 0,
-            .sin6_flowinfo = 0,
-            .sin6_addr = IN6ADDR_ANY_INIT,
-            .sin6_scope_id = 0,
-    };
-    auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
-    len = sizeof(sin6);
-    if (bind(fd, sa, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "bind(IN6ADDR_ANY): %s", strerror(errno));
-        return;
-    }
-
-    // Join the all-routers multicast group, ff02::2%index.
-    struct ipv6_mreq all_rtrs = {
-        .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
-        .ipv6mr_interface = ifIndex,
-    };
-    len = sizeof(all_rtrs);
-    if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
-        jniThrowExceptionFmt(env, "java/net/SocketException",
-                "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
-        return;
-    }
-}
 
 static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
 {
@@ -370,7 +276,6 @@
     { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
     { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
     { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
-    { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
     { "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
     { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
     { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult },
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 6e0d5d8..566c385 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -50,6 +50,7 @@
 #include <memunreachable/memunreachable.h>
 #include <android-base/strings.h>
 #include "android_os_Debug.h"
+#include <vintf/VintfObject.h>
 
 namespace android
 {
@@ -236,7 +237,7 @@
     return err;
 }
 
-static void load_maps(int pid, stats_t* stats, bool* foundSwapPss)
+static bool load_maps(int pid, stats_t* stats, bool* foundSwapPss)
 {
     *foundSwapPss = false;
     uint64_t prev_end = 0;
@@ -406,17 +407,19 @@
         }
     };
 
-    meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
+    return meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
 }
 
-static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
+static jboolean android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
         jint pid, jobject object)
 {
     bool foundSwapPss;
     stats_t stats[_NUM_HEAP];
     memset(&stats, 0, sizeof(stats));
 
-    load_maps(pid, stats, &foundSwapPss);
+    if (!load_maps(pid, stats, &foundSwapPss)) {
+        return JNI_FALSE;
+    }
 
     struct graphics_memory_pss graphics_mem;
     if (read_memtrack_memory(pid, &graphics_mem) == 0) {
@@ -461,7 +464,7 @@
 
     jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
     if (otherArray == NULL) {
-        return;
+        return JNI_FALSE;
     }
 
     int j=0;
@@ -478,6 +481,7 @@
     }
 
     env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
+    return JNI_TRUE;
 }
 
 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
@@ -507,6 +511,8 @@
         rss += stats.rss;
         swapPss = stats.swap_pss;
         pss += swapPss; // Also in swap, those pages would be accounted as Pss without SWAP
+    } else {
+        return 0;
     }
 
     if (outUssSwapPssRss != NULL) {
@@ -835,6 +841,23 @@
     return ionPss;
 }
 
+static jboolean android_os_Debug_isVmapStack(JNIEnv *env, jobject clazz)
+{
+    static enum {
+        CONFIG_UNKNOWN,
+        CONFIG_SET,
+        CONFIG_UNSET,
+    } cfg_state = CONFIG_UNKNOWN;
+
+    if (cfg_state == CONFIG_UNKNOWN) {
+        const std::map<std::string, std::string> configs =
+            vintf::VintfObject::GetInstance()->getRuntimeInfo()->kernelConfigs();
+        std::map<std::string, std::string>::const_iterator it = configs.find("CONFIG_VMAP_STACK");
+        cfg_state = (it != configs.end() && it->second == "y") ? CONFIG_SET : CONFIG_UNSET;
+    }
+    return cfg_state == CONFIG_SET;
+}
+
 /*
  * JNI registration.
  */
@@ -848,7 +871,7 @@
             (void*) android_os_Debug_getNativeHeapFreeSize },
     { "getMemoryInfo",          "(Landroid/os/Debug$MemoryInfo;)V",
             (void*) android_os_Debug_getDirtyPages },
-    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)V",
+    { "getMemoryInfo",          "(ILandroid/os/Debug$MemoryInfo;)Z",
             (void*) android_os_Debug_getDirtyPagesPid },
     { "getPss",                 "()J",
             (void*) android_os_Debug_getPss },
@@ -884,6 +907,8 @@
             (void*)android_os_Debug_getIonPoolsSizeKb },
     { "getIonMappedSizeKb", "()J",
             (void*)android_os_Debug_getIonMappedSizeKb },
+    { "isVmapStack", "()Z",
+            (void*)android_os_Debug_isVmapStack },
 };
 
 int register_android_os_Debug(JNIEnv *env)
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 3531cf2..7daefd3 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -62,7 +62,7 @@
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override;
     void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
     void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
-                               int32_t configId) override;
+                               int32_t configId, nsecs_t vsyncPeriod) override;
 };
 
 
@@ -118,24 +118,23 @@
     mMessageQueue->raiseAndClearException(env, "dispatchHotplug");
 }
 
-void NativeDisplayEventReceiver::dispatchConfigChanged(nsecs_t timestamp,
-                                                       PhysicalDisplayId displayId,
-                                                       int32_t configId) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
+void NativeDisplayEventReceiver::dispatchConfigChanged(
+    nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t) {
+  JNIEnv* env = AndroidRuntime::getJNIEnv();
 
-    ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
-    if (receiverObj.get()) {
-        ALOGV("receiver %p ~ Invoking config changed handler.", this);
-        env->CallVoidMethod(receiverObj.get(),
-                            gDisplayEventReceiverClassInfo.dispatchConfigChanged,
-                            timestamp, displayId, configId);
-        ALOGV("receiver %p ~ Returned from config changed handler.", this);
-    }
+  ScopedLocalRef<jobject> receiverObj(env,
+                                      jniGetReferent(env, mReceiverWeakGlobal));
+  if (receiverObj.get()) {
+    ALOGV("receiver %p ~ Invoking config changed handler.", this);
+    env->CallVoidMethod(receiverObj.get(),
+                        gDisplayEventReceiverClassInfo.dispatchConfigChanged,
+                        timestamp, displayId, configId);
+    ALOGV("receiver %p ~ Returned from config changed handler.", this);
+  }
 
-    mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
+  mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged");
 }
 
-
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
         jobject messageQueueObj, jint vsyncSource, jint configChanged) {
     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index c75c54b..feb9fe3 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -18,7 +18,7 @@
 
 #include <nativehelper/JNIHelp.h>
 
-#include <SkMatrix.h>
+#include <android/graphics/matrix.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
 #include <utils/Log.h>
@@ -571,6 +571,15 @@
     }
 }
 
+static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jobject matrixObj) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+
+    float m[9];
+    AMatrix_getContents(env, matrixObj, m);
+    event->transform(m);
+}
+
 // ----------------- @CriticalNative ------------------------------
 
 static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sourceNativePtr,
@@ -745,24 +754,6 @@
     event->scale(scale);
 }
 
-static void android_view_MotionEvent_nativeTransform(jlong nativePtr, jlong matrixPtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
-
-    static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
-    static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
-    float m[9];
-    matrix->get9(m);
-    event->transform(m);
-}
-
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMotionEventMethods[] = {
@@ -810,6 +801,9 @@
     { "nativeGetAxisValue",
             "(JIII)F",
             (void*)android_view_MotionEvent_nativeGetAxisValue },
+    { "nativeTransform",
+            "(JLandroid/graphics/Matrix;)V",
+            (void*)android_view_MotionEvent_nativeTransform },
 
     // --------------- @CriticalNative ------------------
 
@@ -912,9 +906,6 @@
     { "nativeScale",
             "(JF)V",
             (void*)android_view_MotionEvent_nativeScale },
-    { "nativeTransform",
-            "(JJ)V",
-            (void*)android_view_MotionEvent_nativeTransform },
 };
 
 int register_android_view_MotionEvent(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index df5b02c..d17d0a4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -746,7 +746,13 @@
   const std::string pass_through_source = StringPrintf("/mnt/pass_through/%d", user_id);
   bool isFuse = GetBoolProperty(kPropFuse, false);
 
-  PrepareDir(user_source, DEFAULT_DATA_DIR_PERMISSION, AID_ROOT, AID_ROOT, fail_fn);
+  // Shell is neither AID_ROOT nor AID_EVERYBODY. Since it equally needs 'execute' access to
+  // /mnt/user/0 to 'adb shell ls /sdcard' for instance, we set the uid bit of /mnt/user/0 to
+  // AID_SHELL. This gives shell access along with apps running as group everybody (user 0 apps)
+  // These bits should be consistent with what is set in vold in
+  // Utils#MountUserFuse on FUSE volume mount
+  PrepareDir(user_source, 0710, user_id ? AID_ROOT : AID_SHELL,
+             multiuser_get_uid(user_id, AID_EVERYBODY), fail_fn);
 
   if (isFuse) {
     if (mount_mode == MOUNT_EXTERNAL_PASS_THROUGH || mount_mode ==
diff --git a/core/proto/android/os/cpu_usage.proto b/core/proto/android/os/cpu_usage.proto
new file mode 100644
index 0000000..ac98900
--- /dev/null
+++ b/core/proto/android/os/cpu_usage.proto
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+package android.os;
+
+message CpuUsageProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    message Load {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+        optional float load1 = 1;
+        optional float load5 = 2;
+        optional float load15 = 3;
+    }
+    optional Load current_load = 1;
+
+    optional int64 now = 2;
+    optional int64 last_sample_time = 3;
+    optional int64 current_sample_time = 4;
+    optional int64 last_sample_real_time = 5;
+    optional int64 current_sample_real_time = 6;
+    optional int64 last_sample_wall_time = 7;
+    optional int64 current_sample_wall_time = 8;
+
+    optional int32 total_user_time = 9;
+    optional int32 total_system_time = 10;
+    optional int32 total_iowait_time = 11;
+    optional int32 total_irq_time = 12;
+    optional int32 total_soft_irq_time = 13;
+    optional int32 total_idle_time = 14;
+    optional int32 total_time = 15;
+
+    message Stat {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+        optional int32 uid = 1;
+        optional int32 pid = 2;
+        optional string name = 3;
+        optional bool added = 4;
+        optional bool removed = 5;
+        optional int32 uptime = 6;
+        optional int32 user_time = 7;
+        optional int32 system_time = 8;
+        optional int32 minor_faults = 9;
+        optional int32 major_faults = 10;
+        optional int32 parent_pid = 11;
+    }
+    repeated Stat processes = 16;
+
+    // Next tag: 17
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 6cf9424..8f9c041 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,6 +21,7 @@
 import "frameworks/base/core/proto/android/os/batterytype.proto";
 import "frameworks/base/core/proto/android/os/cpufreq.proto";
 import "frameworks/base/core/proto/android/os/cpuinfo.proto";
+import "frameworks/base/core/proto/android/os/cpu_usage.proto";
 import "frameworks/base/core/proto/android/os/data.proto";
 import "frameworks/base/core/proto/android/os/header.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -469,6 +470,11 @@
         (section).args = "dropbox --proto SubsystemRestart"
     ];
 
+    optional CpuUsageProto process_cpu_usage = 3047 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "cpuinfo --proto"
+    ];
+
     // Reserved for OEMs.
     extensions 50000 to 100000;
 }
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a98d7db..d5384a1 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -115,7 +115,7 @@
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
         optional SettingProto backup_agent_timeout_parameters = 1;
-        optional SettingProto backup_multi_user_enabled = 2;
+        reserved 2; // Used to be backup_multi_user_enabled which was never used
     }
     optional Backup backup = 146;
 
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index ee5144c..9054d54 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -153,4 +153,5 @@
   CROSS_PROFILE_APPS_START_ACTIVITY_AS_USER = 126;
   SET_AUTO_TIME = 127;
   SET_AUTO_TIME_ZONE = 128;
+  SET_PACKAGES_PROTECTED = 129;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 97114b9..31faff6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1623,6 +1623,12 @@
     <permission android:name="android.permission.REQUEST_NETWORK_SCORES"
         android:protectionLevel="signature|setup" />
 
+    <!-- @SystemApi @hide Allows applications to toggle airplane mode.
+         <p>Not for use by third-party or privileged applications.
+    -->
+    <permission android:name="android.permission.NETWORK_AIRPLANE_MODE"
+        android:protectionLevel="signature" />
+
     <!-- Allows network stack services (Connectivity and Wifi) to coordinate
          <p>Not for use by third-party or privileged applications.
          @SystemApi
@@ -2340,13 +2346,9 @@
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:protectionLevel="signature|installer|telephony" />
 
-    <!-- @SystemApi Allows an application to start its own activities, but on a different profile
-         associated with the user. For example, an application running on the main profile of a user
-         can start an activity on a managed profile of that user.
-         This permission is not available to third party applications.
-         @hide -->
+    <!-- Allows interaction across profiles in the same profile group. -->
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|appop|documenter|wellbeing" />
 
     <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
          users on the device. This permission is not available to
@@ -2542,17 +2544,17 @@
     <permission android:name="android.permission.READ_WALLPAPER_INTERNAL"
         android:protectionLevel="signature|privileged" />
 
-    <!-- ============================================ -->
-    <!-- Permissions for changing the system clock -->
-    <!-- ============================================ -->
+    <!-- ===================================================== -->
+    <!-- Permissions for changing the system clock / time zone -->
+    <!-- ===================================================== -->
     <eat-comment />
 
-    <!-- Allows applications to set the system time.
-    <p>Not for use by third-party applications. -->
+    <!-- Allows applications to set the system time directly.
+         <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.SET_TIME"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows applications to set the system time zone.
+    <!-- Allows applications to set the system time zone directly.
          <p>Not for use by third-party applications.
     -->
     <permission android:name="android.permission.SET_TIME_ZONE"
@@ -2560,6 +2562,20 @@
         android:description="@string/permdesc_setTimeZone"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows telephony to suggest the time / time zone.
+         <p>Not for use by third-party applications.
+         @hide
+     -->
+    <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"
+        android:protectionLevel="signature|telephony" />
+
+    <!-- Allows applications like settings to suggest the user's manually chosen time / time zone.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"
+        android:protectionLevel="signature" />
+
     <!-- ==================================================== -->
     <!-- Permissions related to changing status bar   -->
     <!-- ==================================================== -->
@@ -3599,6 +3615,11 @@
     <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to start and stop one time permission sessions
+    @hide -->
+    <permission android:name="android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS"
+                android:protectionLevel="signature|installer" />
+
     <!-- @SystemApi Allows an application to manage the holders of a role.
          @hide
          STOPSHIP b/145526313: Remove wellbeing protection flag from MANAGE_ROLE_HOLDERS. -->
diff --git a/core/res/res/drawable/ic_delete_item.xml b/core/res/res/drawable/ic_delete_item.xml
new file mode 100644
index 0000000..8a398a4
--- /dev/null
+++ b/core/res/res/drawable/ic_delete_item.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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"
+      android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/ic_open_in_new.xml b/core/res/res/drawable/ic_open_in_new.xml
new file mode 100644
index 0000000..67378c8
--- /dev/null
+++ b/core/res/res/drawable/ic_open_in_new.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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:pathData="M19,19H5V5h7V3H5c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2v-7h-2v7zM14,3v2h3.59l-9.83,9.83 1.41,1.41L19,6.41V10h2V3h-7z"
+      android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
index fddca5a..1edd2cd 100644
--- a/core/res/res/layout/accessibility_button_chooser_item.xml
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -21,10 +21,10 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:gravity="center"
-    android:paddingBottom="12dp"
-    android:paddingEnd="16dp"
     android:paddingStart="16dp"
-    android:paddingTop="12dp">
+    android:paddingEnd="16dp"
+    android:paddingTop="12dp"
+    android:paddingBottom="12dp">
 
     <ImageView
         android:id="@+id/accessibility_button_target_icon"
@@ -39,5 +39,26 @@
         android:layout_marginStart="8dp"
         android:layout_weight="1"
         android:textColor="?attr/textColorPrimary"/>
+
+    <FrameLayout
+        android:id="@+id/accessibility_button_target_item_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:minWidth="56dp">
+
+        <ImageView
+            android:id="@+id/accessibility_button_target_view_item"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"/>
+
+        <Switch android:id="@+id/accessibility_button_target_switch_item"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:background="@null"
+                android:clickable="false"
+                android:focusable="false"/>
+    </FrameLayout>
 </LinearLayout>
 
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index eb1f553..68708d35 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -440,7 +440,7 @@
     <string name="permlab_callPhone" msgid="1798582257194643320">"اتصال مباشر بأرقام الهواتف"</string>
     <string name="permdesc_callPhone" msgid="5439809516131609109">"للسماح للتطبيق بطلب أرقام هاتفية بدون تدخل منك. وقد يؤدي ذلك إلى تحمل رسوم غير متوقعة أو إجراء مكالمات غير متوقعة. ومن الجدير بالذكر أن ذلك لا يتيح للتطبيق الاتصال بأرقام الطوارئ. وقد تؤدي التطبيقات الضارة إلى تحملك تكاليف مالية من خلال إجراء مكالمات بدون موافقة منك."</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"الوصول إلى خدمة الاتصال عبر الرسائل الفورية"</string>
-    <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"للسماح للتطبيق باستخدام خدمة الرسائل الفورية لإجراء المكالمات دون تدخل منك."</string>
+    <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"للسماح للتطبيق باستخدام خدمة الرسائل الفورية لإجراء المكالمات بدون تدخل منك."</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"قراءة حالة الهاتف والهوية"</string>
     <string name="permdesc_readPhoneState" msgid="7229063553502788058">"للسماح للتطبيق بالدخول إلى ميزات الهاتف في الجهاز. ويتيح هذا الإذن للتطبيق تحديد رقم الهاتف ومعرّفات الجهاز، وما إذا كانت هناك مكالمة نشطة والرقم البعيد الذي تم الاتصال به في المكالمة."</string>
     <string name="permlab_manageOwnCalls" msgid="9033349060307561370">"توجيه المكالمات من خلال النظام"</string>
@@ -1283,7 +1283,7 @@
     <string name="heavy_weight_switcher_text" msgid="6814316627367160126">"للحصول على أداء أفضل، يمكن فتح لعبة واحدة فقط من هذه الألعاب في كل مرة."</string>
     <string name="old_app_action" msgid="725331621042848590">"الرجوع إلى <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="new_app_action" msgid="547772182913269801">"فتح <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
-    <string name="new_app_description" msgid="1958903080400806644">"سيتم إغلاق <xliff:g id="OLD_APP">%1$s</xliff:g> من دون حفظ"</string>
+    <string name="new_app_description" msgid="1958903080400806644">"سيتم إغلاق <xliff:g id="OLD_APP">%1$s</xliff:g> بدون حفظ"</string>
     <string name="dump_heap_notification" msgid="5316644945404825032">"لقد تجاوزت <xliff:g id="PROC">%1$s</xliff:g> حد الذاكرة."</string>
     <string name="dump_heap_ready_notification" msgid="2302452262927390268">"نَسْخ الذاكرة <xliff:g id="PROC">%1$s</xliff:g> جاهز"</string>
     <string name="dump_heap_notification_detail" msgid="8431586843001054050">"تم جمع مقدار كبير من بيانات الذاكرة. انقر للمشاركة."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6928a14..612854c 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1855,11 +1855,9 @@
     <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"<xliff:g id="PRODUCT_NAME">%1$s</xliff:g> से कनेक्ट किया गया"</string>
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"फ़ाइलें देखने के लिए टैप करें"</string>
     <string name="pin_target" msgid="8036028973110156895">"पिन करें"</string>
-    <!-- no translation found for pin_specific_target (7824671240625957415) -->
-    <skip />
+    <string name="pin_specific_target" msgid="7824671240625957415">"<xliff:g id="LABEL">%1$s</xliff:g> को पिन करें"</string>
     <string name="unpin_target" msgid="3963318576590204447">"अनपिन करें"</string>
-    <!-- no translation found for unpin_specific_target (3859828252160908146) -->
-    <skip />
+    <string name="unpin_specific_target" msgid="3859828252160908146">"<xliff:g id="LABEL">%1$s</xliff:g> को अनपिन करें"</string>
     <string name="app_info" msgid="6113278084877079851">"ऐप्लिकेशन की जानकारी"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"डेमो प्रारंभ हो रहा है…"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index aa28bf2..3e23679 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -665,7 +665,7 @@
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Sletter denne brukerens data på dette nettbrettet uten advarsel."</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Tøm dataene til denne brukeren på denne Android TV-enheten uten advarsel."</string>
     <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Sletter denne brukerens data på denne telefonen uten advarsel."</string>
-    <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Angi enhetens globale mellomtjener"</string>
+    <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Angi enhetens globale proxy-tjener"</string>
     <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Angir den globale proxy-tjeneren på enheten som skal brukes når regelen er aktivert. Bare eieren av enheten kan angi den globale proxy-tjeneren."</string>
     <string name="policylab_expirePassword" msgid="6015404400532459169">"Angi utløpstid for skjermlås"</string>
     <string name="policydesc_expirePassword" msgid="9136524319325960675">"Endrer hvor ofte PIN-koden, passordet eller mønsteret til skjermlåsen skal byttes."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index be173dd..7aa91f7 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -497,10 +497,8 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Aplikaciji omogoča ogled konfiguracije Bluetootha tabličnega računalnika ter vzpostavljanje in sprejemanje povezave s seznanjenimi napravami."</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Aplikaciji dovoljuje ogled konfiguracije vmesnika Bluetooth v napravi Android TV ter ustvarjanje in sprejemanje povezav s seznanjenimi napravami."</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Aplikaciji omogoča ogled konfiguracije Bluetootha telefona ter ustvarjanje in sprejemanje povezave s seznanjenimi napravami."</string>
-    <!-- no translation found for permlab_preferredPaymentInfo (5274423844767445054) -->
-    <skip />
-    <!-- no translation found for permdesc_preferredPaymentInfo (8583552469807294967) -->
-    <skip />
+    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Podatki o prednostni storitvi za plačevanje prek povezave NFC"</string>
+    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"Aplikaciji omogoča pridobivanje podatkov o prednostni storitvi za plačevanje prek povezave NFC, kot so registrirani pripomočki in cilj preusmeritve."</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"nadzor nad komunikacijo s tehnologijo bližnjega polja"</string>
     <string name="permdesc_nfc" msgid="8352737680695296741">"Podpira komunikacijo med računalnikom in oznakami, karticami in bralniki komunikacije s tehnologijo bližnjega polja."</string>
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"onemogočanje zaklepanja zaslona"</string>
@@ -1663,12 +1661,6 @@
     <string name="accessibility_shortcut_enabling_service" msgid="6141620395915529473">"Bližnjica funkcij za ljudi s posebnimi potrebami je vklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="1287340429965172651">"Bližnjica funkcij za ljudi s posebnimi potrebami je izklopila <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Za uporabo storitve <xliff:g id="SERVICE_NAME">%1$s</xliff:g> pritisnite obe tipki za glasnost in ju pridržite tri sekunde"</string>
-    <string name="accessibility_button_prompt_text" msgid="1634298854002673171">"Izberite storitev, ki jo želite uporabljati, ko se dotaknete gumba za funkcije za ljudi s posebnimi potrebami:"</string>
-    <string name="accessibility_gesture_prompt_text" msgid="3271927619707898924">"Izberite storitev, ki jo želite zagnati s potezo za ljudi s posebnimi potrebami (vlečenje z dvema prstoma z dna zaslona navzgor):"</string>
-    <string name="accessibility_gesture_3finger_prompt_text" msgid="218295923313037542">"Izberite storitev, ki jo želite zagnati s potezo za ljudi s posebnimi potrebami (vlečenje s tremi prsti z dna zaslona navzgor):"</string>
-    <string name="accessibility_button_instructional_text" msgid="8523635009916665153">"Če želite preklopiti med storitvami, pridržite gumb za funkcije za ljudi s posebnimi potrebami."</string>
-    <string name="accessibility_gesture_instructional_text" msgid="927882482331885974">"Če želite preklopiti med storitvami, z dvema prstoma povlecite navzgor in pridržite."</string>
-    <string name="accessibility_gesture_3finger_instructional_text" msgid="7527523742771203377">"Če želite preklopiti med storitvami, s tremi prsti povlecite navzgor in pridržite."</string>
     <string name="accessibility_magnification_chooser_text" msgid="1502075582164931596">"Povečava"</string>
     <string name="user_switched" msgid="7249833311585228097">"Trenutni uporabnik <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="user_switching_message" msgid="1912993630661332336">"Preklop na uporabnika <xliff:g id="NAME">%1$s</xliff:g> …"</string>
@@ -1927,11 +1919,9 @@
     <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Vzpostavljena povezava z napravo <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Dotaknite se, če si želite ogledati datoteke"</string>
     <string name="pin_target" msgid="8036028973110156895">"Pripenjanje"</string>
-    <!-- no translation found for pin_specific_target (7824671240625957415) -->
-    <skip />
+    <string name="pin_specific_target" msgid="7824671240625957415">"Pripni aplikacijo <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="unpin_target" msgid="3963318576590204447">"Odpenjanje"</string>
-    <!-- no translation found for unpin_specific_target (3859828252160908146) -->
-    <skip />
+    <string name="unpin_specific_target" msgid="3859828252160908146">"Odpni aplikacijo <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="app_info" msgid="6113278084877079851">"Podatki o aplikacijah"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Začenjanje predstavitve …"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index d01e8e7..2cc42d7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -491,8 +491,8 @@
     <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"允許應用程式查看平板電腦的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
     <string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"允許應用程式查看 Android TV 裝置的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
     <string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"允許應用程式查看手機的藍牙設定,以及建立和接受與其他配對裝置的連線。"</string>
-    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"首選 NFC 付款服務資訊"</string>
-    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得首選 NFC 付款服務的資訊 (如已註冊的付款輔助和最終付款對象)。"</string>
+    <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"由用戶允許授權的 NFC 付款服務資訊"</string>
+    <string name="permdesc_preferredPaymentInfo" msgid="8583552469807294967">"允許應用程式取得由用戶允許授權的 NFC 付款服務資訊 (如已註冊的付款輔助功能和最終付款對象)。"</string>
     <string name="permlab_nfc" msgid="1904455246837674977">"控制近距離無線通訊"</string>
     <string name="permdesc_nfc" msgid="8352737680695296741">"允許應用程式使用近距離無線通訊 (NFC) 標記、卡片及讀取程式進行通訊。"</string>
     <string name="permlab_disableKeyguard" msgid="3605253559020928505">"停用螢幕上鎖"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 38d3e9c..9c08728 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3469,6 +3469,8 @@
              device -->
         <attr name="isVrOnly" format="boolean"/>
         <attr name="__removed2" format="boolean" />
+        <!-- Specifies whether the IME supports showing inline suggestions. -->
+        <attr name="supportsInlineSuggestions" format="boolean" />
     </declare-styleable>
 
     <!-- This is the subtype of InputMethod. Subtype can describe locales (for example, en_US and
@@ -8177,6 +8179,9 @@
         <!-- Fully qualified class name of an activity that allows the user to modify
              the settings for this service. -->
         <attr name="settingsActivity" />
+
+        <!-- Specifies whether the AutofillService supports inline suggestions-->
+        <attr name="supportsInlineSuggestions" format="boolean" />
     </declare-styleable>
 
     <!-- Use <code>compatibility-package</code> as a child tag of <code>autofill-service</code>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6350bf6..6435cdd 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1533,7 +1533,8 @@
          <code>com.google.app.<em>appname</em></code>
 
          <p>Inside of the manifest tag, may appear the following tags
-         in any order: {@link #AndroidManifestPermission permission},
+         in any order: {@link #AndroidManifestFeature feature},
+         {@link #AndroidManifestPermission permission},
          {@link #AndroidManifestPermissionGroup permission-group},
          {@link #AndroidManifestPermissionTree permission-tree},
          {@link #AndroidManifestUsesSdk uses-sdk},
@@ -1756,7 +1757,36 @@
 
              The default value is {@code false}. -->
         <attr name="forceQueryable" format="boolean" />
+
+        <!-- If {@code true} indicates that this application is capable of presenting a unified
+             interface representing multiple profiles.
+
+             The default value is {@code false}. -->
+        <attr name="crossProfile" format="boolean" />
     </declare-styleable>
+
+    <!-- The <code>feature</code> tag declares a feature. A feature is a part of an app. E.g.
+    photo sharing app might include a direct messaging component.
+
+    <p>This appears as a child tag of the root {@link #AndroidManifest manifest} tag.
+
+    <p>In case this feature inherits from another feature, this tag can contain one or multiple
+    {@link #AndroidManifestFeatureInheritFrom inherit-from} tags. -->
+    <declare-styleable name="AndroidManifestFeature" parent="AndroidManifest">
+        <!-- Required identifier for a feature. Can be passed to
+        {@link android.content.Context#createFeatureContext} to create a context for this feature
+        -->
+        <attr name="featureId" format="string" />
+        <!-- Required user visible label for a feature. -->
+        <attr name="label" format="string" />
+    </declare-styleable>
+
+    <!-- Declares previously declared features this feature inherits from. -->
+    <declare-styleable name="AndroidManifestFeatureInheritFrom" parent="AndroidManifestFeature">
+        <!-- Identifier of the feature this feature inherits from -->
+        <attr name="featureId" format="string" />
+    </declare-styleable>
+
     <!-- The <code>permission</code> tag declares a security permission that can be
          used to control access from other packages to specific components or
          features in your package (or other packages).  See the
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9073a02..6691d4c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2093,9 +2093,6 @@
     <!-- Number of times to try again with the shorter interval, before backing
          off until the normal polling interval. A value < 0 indicates infinite. -->
     <integer name="config_ntpRetry">3</integer>
-    <!-- If the time difference is greater than this threshold in milliseconds,
-         then update the time. -->
-    <integer name="config_ntpThreshold">5000</integer>
     <!-- Timeout to wait for NTP server response in milliseconds. -->
     <integer name="config_ntpTimeout">5000</integer>
 
@@ -4287,4 +4284,12 @@
 
     <!-- Whether or not to use assistant stream volume separately from music volume -->
     <bool name="config_useAssistantVolume">false</bool>
+
+    <!-- Whether to use a custom Bugreport handling. When true, ACTION_CUSTOM_BUGREPORT_REQUESTED
+         intent is broadcasted on bugreporting chord (instead of the default full bugreport
+         generation). -->
+    <bool name="config_customBugreport">false</bool>
+
+    <!-- Class name of the custom country detector to be used. -->
+    <string name="config_customCountryDetector" translatable="false">com.android.server.location.ComprehensiveCountryDetector</string>
 </resources>
diff --git a/core/res/res/values/cross_profile_apps.xml b/core/res/res/values/cross_profile_apps.xml
new file mode 100644
index 0000000..ab6f20d
--- /dev/null
+++ b/core/res/res/values/cross_profile_apps.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.
+  -->
+<resources>
+    <!--
+    A collection of apps that have been pre-approved for cross-profile communication.
+    These will not require admin consent, but will still require user consent during provisioning.
+    -->
+    <string-array translatable="false" name="cross_profile_apps">
+    </string-array>
+</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0b63518..817ccde 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3004,6 +3004,10 @@
       <public name="resourcesMap" />
       <public name="animatedImageDrawable"/>
       <public name="htmlDescription"/>
+      <public name="preferMinimalPostProcessing"/>
+      <public name="featureId" />
+      <public name="supportsInlineSuggestions" />
+      <public name="crossProfile" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
@@ -3049,10 +3053,6 @@
       <public name="accessibilitySystemActionLockScreen" />
       <public name="accessibilitySystemActionTakeScreenshot" />
     </public-group>
-
-    <public-group type="attr" first-id="0x0101060c">
-      <public name="preferMinimalPostProcessing"/>
-    </public-group>
   <!-- ===============================================================
        DO NOT ADD UN-GROUPED ITEMS HERE
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ed744ba..66267d1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4354,6 +4354,18 @@
         You can change the feature in Settings > Accessibility.
     </string>
 
+    <!-- Text in button that edit the accessibility shortcut menu. [CHAR LIMIT=100] -->
+    <string name="accessibility_shortcut_menu_button">Empty</string>
+
+    <!-- Text in button that edit the accessibility shortcut menu. [CHAR LIMIT=100] -->
+    <string name="edit_accessibility_shortcut_menu_button">Edit</string>
+
+    <!-- Text in button that save the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
+    <string name="save_accessibility_shortcut_menu_button">Save</string>
+
+    <!-- Text in button that cancel the accessibility shortcut menu changed status. [CHAR LIMIT=100] -->
+    <string name="cancel_accessibility_shortcut_menu_button">Cancel</string>
+
     <!-- Text in button that turns off the accessibility shortcut -->
     <string name="disable_accessibility_shortcut">Turn off Shortcut</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e874ac7..01bd510 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -434,7 +434,6 @@
   <java-symbol type="integer" name="config_ntpPollingInterval" />
   <java-symbol type="integer" name="config_ntpPollingIntervalShorter" />
   <java-symbol type="integer" name="config_ntpRetry" />
-  <java-symbol type="integer" name="config_ntpThreshold" />
   <java-symbol type="integer" name="config_ntpTimeout" />
   <java-symbol type="integer" name="config_shortPressOnPowerBehavior" />
   <java-symbol type="integer" name="config_toastDefaultGravity" />
@@ -1259,6 +1258,7 @@
   <java-symbol type="array" name="vendor_disallowed_apps_managed_user" />
   <java-symbol type="array" name="vendor_disallowed_apps_managed_profile" />
   <java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
+  <java-symbol type="array" name="cross_profile_apps" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="default_lock_wallpaper" />
@@ -3207,15 +3207,19 @@
   <java-symbol type="layout" name="accessibility_button_chooser_item" />
   <java-symbol type="id" name="accessibility_button_target_icon" />
   <java-symbol type="id" name="accessibility_button_target_label" />
+  <java-symbol type="id" name="accessibility_button_target_item_container" />
+  <java-symbol type="id" name="accessibility_button_target_view_item" />
+  <java-symbol type="id" name="accessibility_button_target_switch_item" />
   <java-symbol type="string" name="accessibility_magnification_chooser_text" />
-
-  <java-symbol type="string" name="accessibility_gesture_prompt_text" />
-  <java-symbol type="string" name="accessibility_gesture_3finger_prompt_text" />
-  <java-symbol type="string" name="accessibility_gesture_instructional_text" />
-  <java-symbol type="string" name="accessibility_gesture_3finger_instructional_text" />
+  <java-symbol type="string" name="edit_accessibility_shortcut_menu_button" />
+  <java-symbol type="string" name="save_accessibility_shortcut_menu_button" />
+  <java-symbol type="string" name="cancel_accessibility_shortcut_menu_button" />
 
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
 
+  <java-symbol type="drawable" name="ic_delete_item" />
+  <java-symbol type="drawable" name="ic_open_in_new" />
+
   <!-- com.android.internal.widget.RecyclerView -->
   <java-symbol type="id" name="item_touch_helper_previous_elevation"/>
   <java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
@@ -3601,6 +3605,8 @@
 
   <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" />
 
+  <java-symbol type="string" name="config_customCountryDetector" />
+
   <!-- For Foldables -->
   <java-symbol type="bool" name="config_lidControlsDisplayFold" />
   <java-symbol type="string" name="config_foldedArea" />
diff --git a/core/tests/InstantAppResolverTests/Android.bp b/core/tests/InstantAppResolverTests/Android.bp
new file mode 100644
index 0000000..7b01010
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/Android.bp
@@ -0,0 +1,32 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "FrameworksInstantAppResolverTests",
+    srcs: [ "src/**/*.kt" ],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    platform_apis: true,
+    static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "truth-prebuilt",
+    ],
+    test_suites: ["device-tests"],
+}
diff --git a/core/tests/InstantAppResolverTests/AndroidManifest.xml b/core/tests/InstantAppResolverTests/AndroidManifest.xml
new file mode 100644
index 0000000..f95978b
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.app.instantapp.resolver.test"
+    >
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="InstantAppResolverTests"
+        android:targetPackage="android.app.instantapp.resolver.test"
+        />
+
+</manifest>
diff --git a/core/tests/InstantAppResolverTests/AndroidTest.xml b/core/tests/InstantAppResolverTests/AndroidTest.xml
new file mode 100644
index 0000000..fcc6344
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<configuration description="Test module config for InstantAppResolverTests">
+    <option name="test-tag" value="InstantAppResolverTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="FrameworksInstantAppResolverTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="android.app.instantapp.resolver.test" />
+    </test>
+</configuration>
diff --git a/core/tests/InstantAppResolverTests/src/android/app/instantapp/resolver/test/ResolverServiceMethodFallbackTest.kt b/core/tests/InstantAppResolverTests/src/android/app/instantapp/resolver/test/ResolverServiceMethodFallbackTest.kt
new file mode 100644
index 0000000..2a17ef2
--- /dev/null
+++ b/core/tests/InstantAppResolverTests/src/android/app/instantapp/resolver/test/ResolverServiceMethodFallbackTest.kt
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.instantapp.resolver.test
+
+import android.app.InstantAppResolverService
+import android.app.InstantAppResolverService.InstantAppResolutionCallback
+import android.content.Intent
+import android.content.pm.InstantAppRequestInfo
+import android.net.Uri
+import android.os.Bundle
+import android.os.IRemoteCallback
+import android.os.UserHandle
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.ExpectedException
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+import java.util.UUID
+import kotlin.random.Random
+
+private typealias Method = InstantAppResolverService.(InstantAppRequestInfo) -> Unit
+
+@Suppress("max-line-length")
+@RunWith(Parameterized::class)
+class ResolverServiceMethodFallbackTest @Suppress("UNUSED_PARAMETER") constructor(
+    private val version: Int,
+    private val methodList: List<Method>,
+    private val info: InstantAppRequestInfo,
+    // Remaining only used to print human-readable test name
+    name: String,
+    isWebIntent: Boolean
+) {
+
+    companion object {
+        // Since the resolution callback class is final, mock the IRemoteCallback and have it throw
+        // a unique exception to indicate it was called.
+        class TestRemoteCallbackException : Exception()
+
+        private val testIntentWeb = Intent(Intent.ACTION_VIEW,
+                Uri.parse("https://${this::class.java.canonicalName}.com"))
+        private val testIntentNotWeb = Intent(Intent.ACTION_VIEW,
+                Uri.parse("content://${this::class.java.canonicalName}"))
+
+        private val testRemoteCallback = object : IRemoteCallback {
+            override fun sendResult(data: Bundle?) = throw TestRemoteCallbackException()
+            override fun asBinder() = throw UnsupportedOperationException()
+        }
+        private val testResolutionCallback = InstantAppResolutionCallback(0, testRemoteCallback)
+        private val testArray = IntArray(10) { Random.nextInt() }
+        private val testToken = UUID.randomUUID().toString()
+        private val testUser = UserHandle(Integer.MAX_VALUE)
+        private val testInfoWeb = InstantAppRequestInfo(testIntentWeb, testArray, testUser,
+                false, testToken)
+        private val testInfoNotWeb = InstantAppRequestInfo(testIntentNotWeb, testArray, testUser,
+                false, testToken)
+
+        // Each section defines methods versions with later definitions falling back to
+        // earlier definitions. Each block receives an [InstantAppResolverService] and invokes
+        // the appropriate version with the test data defined above.
+        private val infoOne: Method = { onGetInstantAppResolveInfo(testArray, testToken,
+                testResolutionCallback) }
+        private val infoTwo: Method = { onGetInstantAppResolveInfo(it.intent, testArray, testToken,
+                testResolutionCallback) }
+        private val infoThree: Method = { onGetInstantAppResolveInfo(it.intent, testArray, testUser,
+                testToken, testResolutionCallback) }
+        private val infoFour: Method = { onGetInstantAppResolveInfo(it, testResolutionCallback) }
+
+        private val filterOne: Method = { onGetInstantAppIntentFilter(testArray, testToken,
+                testResolutionCallback) }
+        private val filterTwo: Method = { onGetInstantAppIntentFilter(it.intent, testArray,
+                testToken, testResolutionCallback) }
+        private val filterThree: Method = { onGetInstantAppIntentFilter(it.intent, testArray,
+                testUser, testToken, testResolutionCallback) }
+        private val filterFour: Method = { onGetInstantAppIntentFilter(it, testResolutionCallback) }
+
+        private val infoList = listOf(infoOne, infoTwo, infoThree, infoFour)
+        private val filterList = listOf(filterOne, filterTwo, filterThree, filterFour)
+
+        @JvmStatic
+        @Parameterized.Parameters(name = "{3} version {0}, isWeb = {4}")
+        fun parameters(): Array<Array<*>> {
+            // Sanity check that web intent logic hasn't changed
+            assertThat(testInfoWeb.intent.isWebIntent).isTrue()
+            assertThat(testInfoNotWeb.intent.isWebIntent).isFalse()
+
+            // Declare all the possible params
+            val versions = Array(5) { it }
+            val methods = arrayOf("ResolveInfo" to infoList, "IntentFilter" to filterList)
+            val infos = arrayOf(testInfoWeb, testInfoNotWeb)
+
+            // FlatMap params into every possible combination
+            return infos.flatMap { info ->
+                methods.flatMap { (name, methods) ->
+                    versions.map { version ->
+                        arrayOf(version, methods, info, name, info.intent.isWebIntent)
+                    }
+                }
+            }.toTypedArray()
+        }
+    }
+
+    @field:Mock(answer = Answers.CALLS_REAL_METHODS)
+    lateinit var mockService: InstantAppResolverService
+
+    @get:Rule
+    val expectedException = ExpectedException.none()
+
+    @Before
+    fun setUpMocks() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun onGetInstantApp() {
+        if (version == 0) {
+            // No version of the API was implemented, so expect terminal case
+            if (info.intent.isWebIntent) {
+                // If web intent, terminal is total failure
+                expectedException.expect(IllegalStateException::class.java)
+            } else {
+                // Otherwise, terminal is a fail safe by calling [testRemoteCallback]
+                expectedException.expect(TestRemoteCallbackException::class.java)
+            }
+        } else if (version < 2 && !info.intent.isWebIntent) {
+            // Starting from v2, if resolving a non-web intent and a v2+ method isn't implemented,
+            // it fails safely by calling [testRemoteCallback]
+            expectedException.expect(TestRemoteCallbackException::class.java)
+        }
+
+        // Version 1 is the first method (index 0)
+        val methodIndex = version - 1
+
+        // Implement a method if necessary
+        methodList.getOrNull(methodIndex)?.invoke(doNothing().`when`(mockService), info)
+
+        // Call the latest API
+        methodList.last().invoke(mockService, info)
+
+        // Check all methods before implemented method are never called
+        (0 until methodIndex).forEach {
+            methodList[it].invoke(verify(mockService, never()), info)
+        }
+
+        // Check all methods from implemented method are called
+        (methodIndex until methodList.size).forEach {
+            methodList[it].invoke(verify(mockService), info)
+        }
+
+        verifyNoMoreInteractions(mockService)
+    }
+}
diff --git a/core/tests/coretests/res/xml/ime_meta_inline_suggestions.xml b/core/tests/coretests/res/xml/ime_meta_inline_suggestions.xml
new file mode 100644
index 0000000..e67bf63
--- /dev/null
+++ b/core/tests/coretests/res/xml/ime_meta_inline_suggestions.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<input-method
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
+    android:supportsInlineSuggestions="true"
+>
+    <subtype
+        android:label="subtype1"
+        android:imeSubtypeLocale="en_US"
+        android:imeSubtypeMode="keyboard" />
+</input-method>
diff --git a/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
new file mode 100644
index 0000000..9b3d0c9
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/NetworkTimeSuggestionTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.util.TimestampedValue;
+
+import org.junit.Test;
+
+public class NetworkTimeSuggestionTest {
+
+    private static final TimestampedValue<Long> ARBITRARY_TIME =
+            new TimestampedValue<>(1111L, 2222L);
+
+    @Test
+    public void testEquals() {
+        NetworkTimeSuggestion one = new NetworkTimeSuggestion(ARBITRARY_TIME);
+        assertEquals(one, one);
+
+        NetworkTimeSuggestion two = new NetworkTimeSuggestion(ARBITRARY_TIME);
+        assertEquals(one, two);
+        assertEquals(two, one);
+
+        TimestampedValue<Long> differentTime = new TimestampedValue<>(
+                ARBITRARY_TIME.getReferenceTimeMillis() + 1,
+                ARBITRARY_TIME.getValue());
+        NetworkTimeSuggestion three = new NetworkTimeSuggestion(differentTime);
+        assertNotEquals(one, three);
+        assertNotEquals(three, one);
+
+        // DebugInfo must not be considered in equals().
+        one.addDebugInfo("Debug info 1");
+        two.addDebugInfo("Debug info 2");
+        assertEquals(one, two);
+    }
+
+    @Test
+    public void testParcelable() {
+        NetworkTimeSuggestion suggestion = new NetworkTimeSuggestion(ARBITRARY_TIME);
+        assertRoundTripParcelable(suggestion);
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        suggestion.addDebugInfo("This is debug info");
+        NetworkTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
+        assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+    }
+}
diff --git a/core/tests/coretests/src/android/os/ExternalVibrationTest.java b/core/tests/coretests/src/android/os/ExternalVibrationTest.java
new file mode 100644
index 0000000..3b872d5
--- /dev/null
+++ b/core/tests/coretests/src/android/os/ExternalVibrationTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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 junit.framework.Assert.assertEquals;
+
+import static org.mockito.Mockito.mock;
+
+import android.media.AudioAttributes;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ExternalVibrationTest {
+    @Test
+    public void testSerialization() {
+        AudioAttributes audio = new AudioAttributes.Builder().build();
+        IExternalVibrationController controller = mock(IExternalVibrationController.class);
+        ExternalVibration original = new ExternalVibration(
+                123, // uid
+                "pkg",
+                audio,
+                controller);
+        Parcel p = Parcel.obtain();
+        original.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ExternalVibration restored = ExternalVibration.CREATOR.createFromParcel(p);
+        assertEquals(original, restored);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index ae835e4..84c42db 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -20,6 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.testng.Assert.assertThrows;
+
 import android.content.ContentResolver;
 import android.os.Bundle;
 import android.platform.test.annotations.Presubmit;
@@ -44,9 +46,11 @@
     private static final String KEY = "key1";
     private static final String KEY2 = "key2";
     private static final String KEY3 = "key3";
+    private static final String KEY4 = "key4";
     private static final String VALUE = "value1";
     private static final String VALUE2 = "value2";
     private static final String VALUE3 = "value3";
+    private static final String NULL_VALUE = "null";
 
     @After
     public void cleanUp() {
@@ -561,6 +565,78 @@
         assertThat(properties.getFloat("key5", 0f)).isEqualTo(floatValue);
     }
 
+    @Test
+    public void banNamespaceProperties() throws DeviceConfig.BadConfigException {
+        // Given namespace will be permanently banned, thus it needs to be different every time
+        final String namespaceToBan = NAMESPACE + System.currentTimeMillis();
+        Properties properties = new Properties.Builder(namespaceToBan).setString(KEY, VALUE)
+                .setString(KEY4, NULL_VALUE).build();
+        // Set namespace properties
+        DeviceConfig.setProperties(properties);
+        // Ban namespace with related properties
+        DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, namespaceToBan);
+        // Verify given namespace properties are banned
+        assertThrows(DeviceConfig.BadConfigException.class,
+                () -> DeviceConfig.setProperties(properties));
+        // Modify properties and verify we can set them
+        Properties modifiedProperties = new Properties.Builder(namespaceToBan).setString(KEY, VALUE)
+                .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build();
+        DeviceConfig.setProperties(modifiedProperties);
+        modifiedProperties = DeviceConfig.getProperties(namespaceToBan);
+        assertThat(modifiedProperties.getKeyset()).containsExactly(KEY, KEY2, KEY4);
+        assertThat(modifiedProperties.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+        assertThat(modifiedProperties.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+        // Since value is null DEFAULT_VALUE should be returned
+        assertThat(modifiedProperties.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+    }
+
+    @Test
+    public void banEntireDeviceConfig() throws DeviceConfig.BadConfigException {
+        // Given namespaces will be permanently banned, thus they need to be different every time
+        final String namespaceToBan1 = NAMESPACE + System.currentTimeMillis();
+        final String namespaceToBan2 = NAMESPACE + System.currentTimeMillis() + 1;
+
+        // Set namespaces properties
+        Properties properties1 = new Properties.Builder(namespaceToBan1).setString(KEY, VALUE)
+                .setString(KEY4, NULL_VALUE).build();
+        DeviceConfig.setProperties(properties1);
+        Properties properties2 = new Properties.Builder(namespaceToBan2).setString(KEY2, VALUE2)
+                .setString(KEY4, NULL_VALUE).build();
+        DeviceConfig.setProperties(properties2);
+
+        // Ban entire DeviceConfig
+        DeviceConfig.resetToDefaults(Settings.RESET_MODE_PACKAGE_DEFAULTS, null);
+
+        // Verify given namespace properties are banned
+        assertThrows(DeviceConfig.BadConfigException.class,
+                () -> DeviceConfig.setProperties(properties1));
+        assertThrows(DeviceConfig.BadConfigException.class,
+                () -> DeviceConfig.setProperties(properties2));
+
+        // Modify properties and verify we can set them
+        Properties modifiedProperties1 = new Properties.Builder(namespaceToBan1).setString(KEY,
+                VALUE)
+                .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build();
+        DeviceConfig.setProperties(modifiedProperties1);
+        modifiedProperties1 = DeviceConfig.getProperties(namespaceToBan1);
+        assertThat(modifiedProperties1.getKeyset()).containsExactly(KEY, KEY2, KEY4);
+        assertThat(modifiedProperties1.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+        assertThat(modifiedProperties1.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+        // Since value is null DEFAULT_VALUE should be returned
+        assertThat(modifiedProperties1.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+
+        Properties modifiedProperties2 = new Properties.Builder(namespaceToBan2).setString(KEY,
+                VALUE)
+                .setString(KEY4, NULL_VALUE).setString(KEY2, VALUE2).build();
+        DeviceConfig.setProperties(modifiedProperties1);
+        modifiedProperties2 = DeviceConfig.getProperties(namespaceToBan1);
+        assertThat(modifiedProperties2.getKeyset()).containsExactly(KEY, KEY2, KEY4);
+        assertThat(modifiedProperties2.getString(KEY, DEFAULT_VALUE)).isEqualTo(VALUE);
+        assertThat(modifiedProperties2.getString(KEY2, DEFAULT_VALUE)).isEqualTo(VALUE2);
+        // Since value is null DEFAULT_VALUE should be returned
+        assertThat(modifiedProperties2.getString(KEY4, DEFAULT_VALUE)).isEqualTo(DEFAULT_VALUE);
+    }
+
     // TODO(mpape): resolve b/142727848 and re-enable listener tests
 //    @Test
 //    public void onPropertiesChangedListener_setPropertyCallback() throws InterruptedException {
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 2cf6ff3..7af833b 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -102,8 +102,12 @@
 
     @Test
     public void testShow() {
+
+        // Insets source starts out visible
+        mConsumer.hide();
         mConsumer.show();
         assertTrue("Consumer should be visible", mConsumer.isVisible());
+        verify(mSpyInsetsSource).setVisible(eq(false));
         verify(mSpyInsetsSource).setVisible(eq(true));
     }
 
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index f24e232..8718b95 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -54,10 +54,12 @@
         final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta);
 
         assertThat(imi.supportsSwitchingToNextInputMethod(), is(false));
+        assertThat(imi.isInlineSuggestionsEnabled(), is(false));
 
         final InputMethodInfo clone = cloneViaParcel(imi);
 
         assertThat(clone.supportsSwitchingToNextInputMethod(), is(false));
+        assertThat(imi.isInlineSuggestionsEnabled(), is(false));
     }
 
     @Test
@@ -72,6 +74,17 @@
     }
 
     @Test
+    public void testInlineSuggestionsEnabled() throws Exception {
+        final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_inline_suggestions);
+
+        assertThat(imi.isInlineSuggestionsEnabled(), is(true));
+
+        final InputMethodInfo clone = cloneViaParcel(imi);
+
+        assertThat(clone.isInlineSuggestionsEnabled(), is(true));
+    }
+
+    @Test
     public void testIsVrOnly() throws Exception {
         final InputMethodInfo imi = buildInputMethodForTest(R.xml.ime_meta_vr_only);
 
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index b4f2c91..f497db2 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -16,15 +16,23 @@
 
 package android.widget;
 
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.dragOnText;
 import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
+import static android.widget.espresso.TextViewAssertions.hasSelection;
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.replaceText;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
+import static org.hamcrest.Matchers.emptyString;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.view.MotionEvent;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -70,107 +78,247 @@
         onView(withId(R.id.textview)).perform(replaceText(text));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
 
-        // Drag left to right. The cursor should end up at the position where the finger is lifted.
+        // Swipe left to right to drag the cursor. The cursor should end up at the position where
+        // the finger is lifted.
         onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("llo"), text.indexOf("!")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(11));
 
-        // Drag right to left. The cursor should end up at the position where the finger is lifted.
+        // Swipe right to left to drag the cursor. The cursor should end up at the position where
+        // the finger is lifted.
         onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("!"), text.indexOf("llo")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
     }
 
     @Test
     public void testCursorDrag_horizontal_whenTextViewContentsLargerThanScreen() throws Throwable {
-        String text = "Hello world!"
-                + Strings.repeat("\n", 500) + "012345middle"
-                + Strings.repeat("\n", 10) + "012345last";
+        String text = "Hello world!\n\n"
+                + Strings.repeat("Bla\n\n", 200) + "Bye";
         onView(withId(R.id.textview)).perform(replaceText(text));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
 
-        // Drag left to right. The cursor should end up at the position where the finger is lifted.
+        // Swipe left to right to drag the cursor. The cursor should end up at the position where
+        // the finger is lifted.
         onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("llo"), text.indexOf("!")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(11));
 
-        // Drag right to left. The cursor should end up at the position where the finger is lifted.
+        // Swipe right to left to drag the cursor. The cursor should end up at the position where
+        // the finger is lifted.
         onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("!"), text.indexOf("llo")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(2));
     }
 
     @Test
+    public void testCursorDrag_diagonal_whenTextViewContentsFitOnScreen() throws Throwable {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 1; i <= 9; i++) {
+            sb.append("line").append(i).append("\n");
+        }
+        String text = sb.toString();
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+        // Swipe along a diagonal path. This should drag the cursor.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("2")));
+
+        // Swipe along a steeper diagonal path. This should still drag the cursor.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("3")));
+
+        // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
+        // Normally this would trigger a scroll, but since the full view fits on the screen there
+        // is nothing to scroll and the gesture will trigger a selection drag.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("7")));
+        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
+
+        // Swipe right-up along a very steep diagonal path. This should not drag the cursor.
+        // Normally this would trigger a scroll, but since the full view fits on the screen there
+        // is nothing to scroll and the gesture will trigger a selection drag.
+        int index = text.indexOf("line9");
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line7"), text.indexOf("1")));
+        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
+    }
+
+    @Test
     public void testCursorDrag_diagonal_whenTextViewContentsLargerThanScreen() throws Throwable {
         StringBuilder sb = new StringBuilder();
         for (int i = 1; i <= 9; i++) {
             sb.append("line").append(i).append("\n");
         }
-        sb.append(Strings.repeat("0123456789\n\n", 500)).append("Last line");
+        sb.append(Strings.repeat("0123456789\n", 400)).append("Last");
         String text = sb.toString();
         onView(withId(R.id.textview)).perform(replaceText(text));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
 
-        // Drag along a diagonal path.
+        // Swipe along a diagonal path. This should drag the cursor.
         onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("2")));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("2")));
 
-        // Drag along a steeper diagonal path.
-        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("9")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("9")));
+        // Swipe along a steeper diagonal path. This should still drag the cursor.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("3")));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("3")));
 
-        // Drag along an almost vertical path.
-        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
-        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ne1"), text.indexOf("9")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("9")));
+        // Swipe right-down along a very steep diagonal path. This should not drag the cursor.
+        // Normally this would trigger a scroll up, but since the view is already at the top there
+        // is nothing to scroll and the gesture will trigger a selection drag.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line1"), text.indexOf("7")));
+        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
 
-        // Drag along a vertical path from line 1 to line 9.
-        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
-        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("e1"), text.indexOf("e9")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("e9")));
-
-        // Drag along a vertical path from line 9 to line 1.
-        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
-        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("e9"), text.indexOf("e1")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("e1")));
+        // Swipe right-up along a very steep diagonal path. This should not drag the cursor. This
+        // will trigger a downward scroll and the cursor position will not change.
+        int index = text.indexOf("line9");
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("line7"), text.indexOf("1")));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
     }
 
     @Test
     public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable {
-        String text = "012first\n\n" + Strings.repeat("012345\n\n", 10) + "012last";
+        String text = "012345_aaa\n"
+                + "0123456789\n"
+                + "012345_bbb\n"
+                + "0123456789\n"
+                + "012345_ccc\n"
+                + "0123456789\n"
+                + "012345_ddd";
         onView(withId(R.id.textview)).perform(replaceText(text));
+
+        // Swipe up vertically. This should not drag the cursor. Since there's also nothing to
+        // scroll, the gesture will trigger a selection drag.
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("bbb"), text.indexOf("aaa")));
+        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
 
-        // Drag down. Since neither the TextView nor its container require scrolling, the cursor
-        // drag should execute and the cursor should end up at the position where the finger is
-        // lifted.
-        onView(withId(R.id.textview)).perform(
-                dragOnText(text.indexOf("first"), text.indexOf("last")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.length() - 4));
-
-        // Drag up. Since neither the TextView nor its container require scrolling, the cursor
-        // drag should execute and the cursor should end up at the position where the finger is
-        // lifted.
-        onView(withId(R.id.textview)).perform(
-                dragOnText(text.indexOf("last"), text.indexOf("first")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(3));
+        // Swipe down vertically. This should not drag the cursor. Since there's also nothing to
+        // scroll, the gesture will trigger a selection drag.
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(0));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ccc"), text.indexOf("ddd")));
+        onView(withId(R.id.textview)).check(hasSelection(not(emptyString())));
     }
 
     @Test
     public void testCursorDrag_vertical_whenTextViewContentsLargerThanScreen() throws Throwable {
-        String text = "012345first\n\n"
-                + Strings.repeat("0123456789\n\n", 10) + "012345middle"
-                + Strings.repeat("0123456789\n\n", 500) + "012345last";
+        String text = "012345_aaa\n"
+                + "0123456789\n"
+                + "012345_bbb\n"
+                + "0123456789\n"
+                + "012345_ccc\n"
+                + "0123456789\n"
+                + "012345_ddd\n"
+                + Strings.repeat("0123456789\n", 400) + "012345_zzz";
         onView(withId(R.id.textview)).perform(replaceText(text));
-        int initialCursorPosition = 0;
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf("ddd")));
+        int initialCursorPosition = text.indexOf("ddd");
         onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
 
-        // Drag up.
-        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
-        onView(withId(R.id.textview)).perform(
-                dragOnText(text.indexOf("middle"), text.indexOf("first")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("first")));
+        // Swipe up vertically. This should trigger a downward scroll.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("bbb"), text.indexOf("aaa")));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
 
-        // Drag down.
-        // TODO(b/145833335): Consider whether this should scroll instead of dragging the cursor.
-        onView(withId(R.id.textview)).perform(
-                dragOnText(text.indexOf("first"), text.indexOf("middle")));
-        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(text.indexOf("middle")));
+        // Swipe down vertically. This should trigger an upward scroll.
+        onView(withId(R.id.textview)).perform(dragOnText(text.indexOf("ccc"), text.indexOf("ddd")));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(initialCursorPosition));
+    }
+
+    @Test
+    public void testEditor_onTouchEvent_cursorDrag() throws Throwable {
+        String text = "testEditor_onTouchEvent_cursorDrag";
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+
+        // Simulate a tap-and-drag gesture. This should trigger a cursor drag.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+        long event2Time = 1002;
+        MotionEvent event2 = moveEvent(event1Time, event2Time, 21f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+        long event3Time = 1003;
+        MotionEvent event3 = moveEvent(event3Time, event3Time, 120f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
+        mInstrumentation.waitForIdleSync();
+        assertTrue(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+        long event4Time = 1004;
+        MotionEvent event4 = upEvent(event3Time, event4Time, 120f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+    }
+
+    @Test
+    public void testEditor_onTouchEvent_selectionDrag() throws Throwable {
+        String text = "testEditor_onTouchEvent_selectionDrag";
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(0));
+
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+
+        // Simulate a double-tap followed by a drag. This should trigger a selection drag.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event1));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+        long event2Time = 1002;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event2));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+
+        long event3Time = 1003;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 20f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event3));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertTrue(editor.getSelectionController().isCursorBeingModified());
+
+        long event4Time = 1004;
+        MotionEvent event4 = moveEvent(event3Time, event4Time, 120f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event4));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertTrue(editor.getSelectionController().isCursorBeingModified());
+
+        long event5Time = 1005;
+        MotionEvent event5 = upEvent(event3Time, event5Time, 120f, 30f);
+        mActivity.runOnUiThread(() -> editor.onTouchEvent(event5));
+        mInstrumentation.waitForIdleSync();
+        assertFalse(editor.getInsertionController().isCursorBeingModified());
+        assertFalse(editor.getSelectionController().isCursorBeingModified());
+    }
+
+    private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
+        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+    }
+
+    private static MotionEvent upEvent(long downTime, long eventTime, float x, float y) {
+        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+    }
+
+    private static MotionEvent moveEvent(long downTime, long eventTime, float x, float y) {
+        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_MOVE, x, y, 0);
     }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index c3ef1c8..fbe4c1a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -1022,23 +1022,11 @@
 
     @Test
     public void testSelectionMetricsLogger_abandonEventIncludesEntityType() throws Throwable {
-        final List<SelectionEvent> selectionEvents = new ArrayList<>();
-        final TextClassifier classifier = new TextClassifier() {
-            @Override
-            public void onSelectionEvent(SelectionEvent event) {
-                selectionEvents.add(event);
-            }
-
-            @Override
-            public TextSelection suggestSelection(TextSelection.Request request) {
-                return new TextSelection.Builder(request.getStartIndex(), request.getEndIndex())
-                        .setEntityType(TextClassifier.TYPE_PHONE, 1)
-                        .build();
-            }
-        };
+        final TestableTextClassifier classifier = new TestableTextClassifier();
         final TextView textView = mActivity.findViewById(R.id.textview);
         mActivityRule.runOnUiThread(() -> textView.setTextClassifier(classifier));
         mInstrumentation.waitForIdleSync();
+
         final String text = "My number is 987654321";
 
         onView(withId(R.id.textview)).perform(replaceText(text));
@@ -1053,6 +1041,7 @@
         long waitTime = 0;
         SelectionEvent lastEvent;
         do {
+            final List<SelectionEvent> selectionEvents = classifier.getSelectionEvents();
             lastEvent = selectionEvents.get(selectionEvents.size() - 1);
             if (lastEvent.getEventType() == SelectionEvent.ACTION_ABANDON) {
                 break;
@@ -1061,6 +1050,29 @@
             waitTime += pollInterval;
         } while (waitTime < abandonDelay * 10);
         assertEquals(SelectionEvent.ACTION_ABANDON, lastEvent.getEventType());
+    }
+
+    @Test
+    public void testSelectionMetricsLogger_overtypeEventIncludesEntityType() throws Throwable {
+        final TestableTextClassifier classifier = new TestableTextClassifier();
+        final TextView textView = mActivity.findViewById(R.id.textview);
+        mActivityRule.runOnUiThread(() -> textView.setTextClassifier(classifier));
+        mInstrumentation.waitForIdleSync();
+
+        final String text = "My number is 987654321";
+
+        // Long press to trigger selection
+        onView(withId(R.id.textview)).perform(replaceText(text));
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('9')));
+        sleepForFloatingToolbarPopup();
+
+        // Type over the selection
+        onView(withId(R.id.textview)).perform(pressKey(KeyEvent.KEYCODE_A));
+        mInstrumentation.waitForIdleSync();
+
+        final List<SelectionEvent> selectionEvents = classifier.getSelectionEvents();
+        final SelectionEvent lastEvent = selectionEvents.get(selectionEvents.size() - 1);
+        assertEquals(SelectionEvent.ACTION_OVERTYPE, lastEvent.getEventType());
         assertEquals(TextClassifier.TYPE_PHONE, lastEvent.getEntityType());
     }
 
@@ -1115,4 +1127,24 @@
     private enum TextStyle {
         PLAIN, STYLED
     }
+
+    private final class TestableTextClassifier implements TextClassifier {
+        final List<SelectionEvent> mSelectionEvents = new ArrayList<>();
+
+        @Override
+        public void onSelectionEvent(SelectionEvent event) {
+            mSelectionEvents.add(event);
+        }
+
+        @Override
+        public TextSelection suggestSelection(TextSelection.Request request) {
+            return new TextSelection.Builder(request.getStartIndex(), request.getEndIndex())
+                    .setEntityType(TextClassifier.TYPE_PHONE, 1)
+                    .build();
+        }
+
+        List<SelectionEvent> getSelectionEvents() {
+            return mSelectionEvents;
+        }
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index 8622b7e..c086421 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -54,6 +54,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager.ShareShortcutInfo;
+import android.content.res.Configuration;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -977,6 +978,7 @@
                         serviceTargets,
                         TARGET_TYPE_CHOOSER_TARGET)
         );
+
         // Thread.sleep shouldn't be a thing in an integration test but it's
         // necessary here because of the way the code is structured
         // TODO: restructure the tests b/129870719
@@ -1075,7 +1077,29 @@
 
     // This test is too long and too slow and should not be taken as an example for future tests.
     @Test
-    public void testDirectTargetLoggingWithAppTargetNotRanked() throws InterruptedException {
+    public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
+            throws InterruptedException {
+        testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_PORTRAIT, 4);
+    }
+
+    @Test
+    public void testDirectTargetLoggingWithAppTargetNotRankedLandscape()
+            throws InterruptedException {
+        testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_LANDSCAPE, 8);
+    }
+
+    private void testDirectTargetLoggingWithAppTargetNotRanked(
+            int orientation, int appTargetsExpected
+    ) throws InterruptedException {
+        Configuration configuration =
+                new Configuration(InstrumentationRegistry.getInstrumentation().getContext()
+                        .getResources().getConfiguration());
+        configuration.orientation = orientation;
+
+        sOverrides.resources = Mockito.spy(
+                InstrumentationRegistry.getInstrumentation().getContext().getResources());
+        when(sOverrides.resources.getConfiguration()).thenReturn(configuration);
+
         Intent sendIntent = createSendTextIntent();
         // We need app targets for direct targets to get displayed
         List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15);
@@ -1111,8 +1135,10 @@
         // TODO: restructure the tests b/129870719
         Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
 
-        assertThat("Chooser should have 20 targets (4 apps, 1 direct, 15 A-Z)",
-                activity.getAdapter().getCount(), is(20));
+        assertThat(
+                String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)",
+                        appTargetsExpected + 16, appTargetsExpected),
+                activity.getAdapter().getCount(), is(appTargetsExpected + 16));
         assertThat("Chooser should have exactly one selectable direct target",
                 activity.getAdapter().getSelectableServiceTargetCount(), is(1));
         assertThat("The resolver info must match the resolver info used to create the target",
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 9863e38..66d84aa 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -54,7 +54,6 @@
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -76,7 +75,6 @@
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 @Presubmit
-@FlakyTest(bugId = 143153552)
 public class ActivityThreadClientTest {
 
     @Test
diff --git a/core/xsd/permission.xsd b/core/xsd/permission.xsd
index 9520db7..cc01a31 100644
--- a/core/xsd/permission.xsd
+++ b/core/xsd/permission.xsd
@@ -126,12 +126,12 @@
     </xs:complexType>
     <xs:complexType name="privapp-permissions">
         <xs:sequence>
-            <xs:element name="permission" maxOccurs="unbounded">
+            <xs:element name="permission" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
                     <xs:attribute name="name" type="xs:string"/>
                 </xs:complexType>
             </xs:element>
-            <xs:element name="deny-permission" maxOccurs="unbounded">
+            <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
                     <xs:attribute name="name" type="xs:string"/>
                 </xs:complexType>
@@ -141,12 +141,12 @@
     </xs:complexType>
     <xs:complexType name="oem-permissions">
         <xs:sequence>
-            <xs:element name="permission" maxOccurs="unbounded">
+            <xs:element name="permission" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
                     <xs:attribute name="name" type="xs:string"/>
                 </xs:complexType>
             </xs:element>
-            <xs:element name="deny-permission" maxOccurs="unbounded">
+            <xs:element name="deny-permission" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
                     <xs:attribute name="name" type="xs:string"/>
                 </xs:complexType>
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index ee989cc..70b61e0 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -42,8 +42,8 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
         <permission name="android.permission.REBOOT"/>
-        <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.STATUS_BAR"/>
+        <permission name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"/>
         <permission name="android.permission.TETHER_PRIVILEGED"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.USER_ACTIVITY"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f1941fc..eb1d1ab 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -130,7 +130,6 @@
         <permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
-        <permission name="android.permission.PACKAGE_USAGE_STATS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
@@ -161,12 +160,12 @@
         <permission name="android.permission.REGISTER_CALL_PROVIDER"/>
         <permission name="android.permission.REGISTER_SIM_SUBSCRIPTION"/>
         <permission name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
-        <permission name="android.permission.SET_TIME"/>
         <permission name="android.permission.SET_TIME_ZONE"/>
         <permission name="android.permission.SHUTDOWN"/>
         <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
         <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.STOP_APP_SWITCHES"/>
+        <permission name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"/>
         <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
         <permission name="android.permission.UPDATE_DEVICE_STATS"/>
         <permission name="android.permission.UPDATE_LOCK"/>
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
new file mode 100644
index 0000000..b6c0765
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-11-ratio.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
new file mode 100644
index 0000000..4e975e5
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-169-ratio.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
new file mode 100644
index 0000000..a331095
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-ratio.png
Binary files differ
diff --git a/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
new file mode 100644
index 0000000..41e6668
--- /dev/null
+++ b/docs/html/reference/images/camera2/metadata/android.scaler.cropRegion/crop-region-43-square-ratio.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 6e7f286..bee8d5e 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -21,7 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.Canvas.VertexMode;
 import android.graphics.text.MeasuredText;
 import android.text.GraphicsOperations;
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index d900a42..ac094ba 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,8 +21,8 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.ResourcesImpl;
 import android.hardware.HardwareBuffer;
 import android.os.Build;
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index 5623a8a..bad487b 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -20,7 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.os.Trace;
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 629d8c1..34eba97 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -15,7 +15,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.os.Build;
 
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 198d1e7..edf53c4 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -17,7 +17,7 @@
 package android.graphics;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Shader used to draw a bitmap as a texture. The bitmap can be repeated or
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index cbd4ead..80a3740 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A camera instance can be used to compute 3D transformations and
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a815f20..9a0ca3e 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -22,7 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.text.MeasuredText;
 import android.os.Build;
 
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index 1275e08..4263772c 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,7 +16,8 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import com.android.internal.util.VirtualRefBasePtr;
 
 /**
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 0f7980c..a8b18a9 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A color filter that transforms colors through a 4x5 color matrix. This filter
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 5ad93f4..ae90995 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,7 +17,7 @@
 package android.graphics;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontVariationAxis;
 import android.text.TextUtils;
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index 21cc375..c146bbd 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.text.FontConfig;
 import android.util.Xml;
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index 3b1fc70..99fa5ee 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index aecef8e..83432c3 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -28,8 +28,8 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
@@ -277,6 +277,10 @@
                     assetFd = mResolver.openAssetFileDescriptor(mUri, "r");
                 }
             } catch (FileNotFoundException e) {
+                // Handled below, along with the case where assetFd was set to null.
+            }
+
+            if (assetFd == null) {
                 // Some images cannot be opened as AssetFileDescriptors (e.g.
                 // bmp, ico). Open them as InputStreams.
                 InputStream is = mResolver.openInputStream(mUri);
@@ -286,9 +290,7 @@
 
                 return createFromStream(is, true, preferAnimation, this);
             }
-            if (assetFd == null) {
-                throw new FileNotFoundException(mUri.toString());
-            }
+
             return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
         }
     }
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index 62a890f..221dfa1 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -22,7 +22,7 @@
 package android.graphics;
 
 import android.annotation.ColorInt;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A color filter that can be used to simulate simple lighting effects.
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 12e63c0..3f3ad96 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -20,7 +20,7 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 
 public class LinearGradient extends Shader {
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 22b6401..cf914c2 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
diff --git a/graphics/java/android/graphics/Movie.java b/graphics/java/android/graphics/Movie.java
index 6f030ff..4b3924f 100644
--- a/graphics/java/android/graphics/Movie.java
+++ b/graphics/java/android/graphics/Movie.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.os.Build;
 
diff --git a/graphics/java/android/graphics/NinePatch.java b/graphics/java/android/graphics/NinePatch.java
index c4c1eac..ff32393 100644
--- a/graphics/java/android/graphics/NinePatch.java
+++ b/graphics/java/android/graphics/NinePatch.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * The NinePatch class permits drawing a bitmap in nine or more sections.
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 1fc056c..91a60c3 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -19,7 +19,7 @@
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.drawable.Drawable;
 
 import java.lang.annotation.Retention;
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 109d863..3b58624 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -24,7 +24,7 @@
 import android.annotation.Nullable;
 import android.annotation.Px;
 import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.graphics.fonts.FontVariationAxis;
 import android.os.Build;
 import android.os.LocaleList;
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 7282d52..1362fd8 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Size;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index 8d12cbf..390d3d4 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -17,7 +17,7 @@
 package android.graphics;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.io.InputStream;
 import java.io.OutputStream;
diff --git a/graphics/java/android/graphics/PorterDuff.java b/graphics/java/android/graphics/PorterDuff.java
index bc1f66f..1275cb9 100644
--- a/graphics/java/android/graphics/PorterDuff.java
+++ b/graphics/java/android/graphics/PorterDuff.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * <p>This class contains the list of alpha compositing and blending modes
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index cc2d3a8..50ecb62 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -18,7 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A color filter that can be used to tint the source pixels using a single
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index acbe3da..96b7b9a 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -20,7 +20,7 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 public class RadialGradient extends Shader {
     @UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java
index 9e1946c..081b851 100644
--- a/graphics/java/android/graphics/Rect.java
+++ b/graphics/java/android/graphics/Rect.java
@@ -19,7 +19,7 @@
 import android.annotation.CheckResult;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
diff --git a/graphics/java/android/graphics/Region.java b/graphics/java/android/graphics/Region.java
index ec7f7a0..d8d9641 100644
--- a/graphics/java/android/graphics/Region.java
+++ b/graphics/java/android/graphics/Region.java
@@ -17,7 +17,7 @@
 package android.graphics;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Pools.SynchronizedPool;
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 613ce90..5a3f2a9 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -34,6 +34,7 @@
     }
 
     private byte[] mUniforms;
+    private boolean mIsOpaque;
 
     /**
      * Current native shader factory instance.
@@ -56,7 +57,8 @@
             ColorSpace colorSpace) {
         super(colorSpace);
         mUniforms = uniforms;
-        mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl, isOpaque);
+        mIsOpaque = isOpaque;
+        mNativeInstanceRuntimeShaderFactory = nativeCreateShaderFactory(sksl);
         NoImagePreloadHolder.sRegistry.registerNativeAllocation(this,
                 mNativeInstanceRuntimeShaderFactory);
     }
@@ -75,13 +77,13 @@
     @Override
     long createNativeInstance(long nativeMatrix) {
         return nativeCreate(mNativeInstanceRuntimeShaderFactory, nativeMatrix, mUniforms,
-                colorSpace().getNativeInstance());
+                colorSpace().getNativeInstance(), mIsOpaque);
     }
 
     private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
-            long colorSpaceHandle);
+            long colorSpaceHandle, boolean isOpaque);
 
-    private static native long nativeCreateShaderFactory(String sksl, boolean isOpaque);
+    private static native long nativeCreateShaderFactory(String sksl);
 
     private static native long nativeGetFinalizer();
 }
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 3050d1d..5335aa4 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -20,7 +20,7 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import libcore.util.NativeAllocationRegistry;
 
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 99f440d..697daa8 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -17,7 +17,7 @@
 package android.graphics;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 667f45a..0852004 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -20,7 +20,7 @@
 import android.annotation.ColorLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 public class SweepGradient extends Shader {
     @UnsupportedAppUsage
diff --git a/graphics/java/android/graphics/TableMaskFilter.java b/graphics/java/android/graphics/TableMaskFilter.java
index d81c491..204f970 100644
--- a/graphics/java/android/graphics/TableMaskFilter.java
+++ b/graphics/java/android/graphics/TableMaskFilter.java
@@ -16,7 +16,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index 0ae2c70..ef3f7f7 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -16,7 +16,8 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+
 import com.android.internal.util.ArrayUtils;
 
 /**
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 6d20ec3..a2dd9a8 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -25,7 +25,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetManager;
 import android.graphics.fonts.Font;
 import android.graphics.fonts.FontFamily;
diff --git a/graphics/java/android/graphics/Xfermode.java b/graphics/java/android/graphics/Xfermode.java
index 6f4adfd..e79fb76 100644
--- a/graphics/java/android/graphics/Xfermode.java
+++ b/graphics/java/android/graphics/Xfermode.java
@@ -21,7 +21,7 @@
 
 package android.graphics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Xfermode is the base class for objects that are called to implement custom
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index 82f5870..d894600 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -19,7 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
diff --git a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
index b29fd4d..686f146 100644
--- a/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedRotateDrawable.java
@@ -18,23 +18,23 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.TypedValue;
-import android.os.SystemClock;
+
+import com.android.internal.R;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 
-import com.android.internal.R;
-
 /**
  * @hide
  */
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
index 11a46c4..06159d8 100644
--- a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -20,7 +20,7 @@
 import android.animation.TimeInterpolator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 66947da..1acf6c5 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -25,9 +25,9 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.app.Application;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 57764c2..8c3fa44 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -16,20 +16,20 @@
 
 package android.graphics.drawable;
 
-import com.android.internal.R;
+import android.annotation.NonNull;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.os.SystemClock;
+import android.util.AttributeSet;
 
-import java.io.IOException;
+import com.android.internal.R;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
-import android.os.SystemClock;
-import android.util.AttributeSet;
+import java.io.IOException;
 
 /**
  * An object used to create frame-by-frame animations, defined by a series of
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index e4aa774..4e768c9 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -17,7 +17,7 @@
 package android.graphics.drawable;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index 31fdb02..69ed9b4 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -16,21 +16,23 @@
 
 package android.graphics.drawable;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.Gravity;
+
 import com.android.internal.R;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.Resources.Theme;
-import android.graphics.*;
-import android.view.Gravity;
-import android.util.AttributeSet;
-
 import java.io.IOException;
 
 /**
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
index c6515ef..feb6101 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
@@ -176,7 +176,7 @@
                 KeyStore keyStore, IBinder operationToken) {
             KeyStoreCryptoOperationStreamer streamer = new KeyStoreCryptoOperationChunkedStreamer(
                     new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
-                            keyStore, operationToken));
+                            keyStore, operationToken), 0);
             if (isEncrypting()) {
                 return streamer;
             } else {
@@ -191,7 +191,7 @@
         protected final KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
                 KeyStore keyStore, IBinder operationToken) {
             return new KeyStoreCryptoOperationChunkedStreamer(
-                    new AdditionalAuthenticationDataStream(keyStore, operationToken));
+                    new AdditionalAuthenticationDataStream(keyStore, operationToken), 0);
         }
 
         @Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 5bcb34a..ccc3153 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -299,7 +299,7 @@
             KeyStore keyStore, IBinder operationToken) {
         return new KeyStoreCryptoOperationChunkedStreamer(
                 new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
-                        keyStore, operationToken));
+                        keyStore, operationToken), 0);
     }
 
     /**
diff --git a/keystore/java/android/security/keystore/ArrayUtils.java b/keystore/java/android/security/keystore/ArrayUtils.java
index 26172d2..f519c7c 100644
--- a/keystore/java/android/security/keystore/ArrayUtils.java
+++ b/keystore/java/android/security/keystore/ArrayUtils.java
@@ -55,6 +55,34 @@
         }
     }
 
+    /**
+     * Copies a subset of the source array to the destination array.
+     * Length will be limited to the bounds of source and destination arrays.
+     * The length actually copied is returned, which will be <= length argument.
+     * @param src is the source array
+     * @param srcOffset is the offset in the source array.
+     * @param dst is the destination array.
+     * @param dstOffset is the offset in the destination array.
+     * @param length is the length to be copied from source to destination array.
+     * @return The length actually copied from source array.
+     */
+    public static int copy(byte[] src, int srcOffset, byte[] dst, int dstOffset, int length) {
+        if (dst == null || src == null) {
+            return 0;
+        }
+        if (length > dst.length - dstOffset) {
+            length = dst.length - dstOffset;
+        }
+        if (length > src.length - srcOffset) {
+            length = src.length - srcOffset;
+        }
+        if (length <= 0) {
+            return 0;
+        }
+        System.arraycopy(src, srcOffset, dst, dstOffset, length);
+        return length;
+    }
+
     public static byte[] subarray(byte[] arr, int offset, int len) {
         if (len == 0) {
             return EmptyArray.BYTE;
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 75bea26..2c0f40d 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -24,19 +24,20 @@
 
 import libcore.util.EmptyArray;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.ProviderException;
-
 /**
  * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
  * {@code update} and {@code finish} operations.
  *
- * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * <p>The helper abstracts away issues that need to be solved in most code that uses KeyStore's
  * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
  * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
  * operation may consume less data than provided, in which case the caller has to buffer the
- * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * remainder for next time. Thirdly, when the input is smaller than a threshold, skipping update
+ * and passing input data directly to final improves performance. This threshold is configurable;
+ * using a threshold <= 1 causes the helper act eagerly, which may be required for some types of
+ * operations (e.g. ciphers).
+ *
+ * <p>The helper exposes {@link #update(byte[], int, int) update} and
  * {@link #doFinal(byte[], int, int, byte[], byte[]) doFinal} operations which can be used to
  * conveniently implement various JCA crypto primitives.
  *
@@ -67,240 +68,122 @@
 
     // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
     // Thus, it's safer to use a much smaller upper bound.
-    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
+    private static final int DEFAULT_CHUNK_SIZE_MAX = 64 * 1024;
+    // The chunk buffer will be sent to update until its size under this threshold.
+    // This threshold should be <= the max input allowed for finish.
+    // Setting this threshold <= 1 will effectivley disable buffering between updates.
+    private static final int DEFAULT_CHUNK_SIZE_THRESHOLD = 2 * 1024;
 
     private final Stream mKeyStoreStream;
-    private final int mMaxChunkSize;
-
-    private byte[] mBuffered = EmptyArray.BYTE;
-    private int mBufferedOffset;
-    private int mBufferedLength;
+    private final int mChunkSizeMax;
+    private final int mChunkSizeThreshold;
+    private final byte[] mChunk;
+    private int mChunkLength = 0;
     private long mConsumedInputSizeBytes;
     private long mProducedOutputSizeBytes;
 
-    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
-        this(operation, DEFAULT_MAX_CHUNK_SIZE);
+    KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
+        this(operation, DEFAULT_CHUNK_SIZE_THRESHOLD, DEFAULT_CHUNK_SIZE_MAX);
     }
 
-    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
+    KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold) {
+        this(operation, chunkSizeThreshold, DEFAULT_CHUNK_SIZE_MAX);
+    }
+
+    KeyStoreCryptoOperationChunkedStreamer(Stream operation, int chunkSizeThreshold,
+            int chunkSizeMax) {
         mKeyStoreStream = operation;
-        mMaxChunkSize = maxChunkSize;
+        mChunkSizeMax = chunkSizeMax;
+        if (chunkSizeThreshold <= 0) {
+            mChunkSizeThreshold = 1;
+        } else if (chunkSizeThreshold > chunkSizeMax) {
+            mChunkSizeThreshold = chunkSizeMax;
+        } else {
+            mChunkSizeThreshold = chunkSizeThreshold;
+        }
+        mChunk = new byte[mChunkSizeMax];
     }
 
-    @Override
     public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
-        if (inputLength == 0) {
+        if (inputLength == 0 || input == null) {
             // No input provided
             return EmptyArray.BYTE;
         }
+        if (inputLength < 0 || inputOffset < 0 || (inputOffset + inputLength) > input.length) {
+            throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+                "Input offset and length out of bounds of input array");
+        }
 
-        ByteArrayOutputStream bufferedOutput = null;
+        byte[] output = EmptyArray.BYTE;
 
-        while (inputLength > 0) {
-            byte[] chunk;
-            int inputBytesInChunk;
-            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
-                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
-                // update operation.
-                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
-                chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
-                        input, inputOffset, inputBytesInChunk);
-            } else {
-                // All of available input fits into one chunk.
-                if ((mBufferedLength == 0) && (inputOffset == 0)
-                        && (inputLength == input.length)) {
-                    // Nothing buffered and all of input array needs to be fed into the update
-                    // operation.
-                    chunk = input;
-                    inputBytesInChunk = input.length;
-                } else {
-                    // Need to combine buffered data with input data into one array.
-                    inputBytesInChunk = inputLength;
-                    chunk = ArrayUtils.concat(mBuffered, mBufferedOffset, mBufferedLength,
-                            input, inputOffset, inputBytesInChunk);
+        while (inputLength > 0 || mChunkLength >= mChunkSizeThreshold) {
+            int inputConsumed = ArrayUtils.copy(input, inputOffset, mChunk, mChunkLength,
+                    inputLength);
+            inputLength -= inputConsumed;
+            inputOffset += inputConsumed;
+            mChunkLength += inputConsumed;
+            mConsumedInputSizeBytes += inputConsumed;
+
+            if (mChunkLength > mChunkSizeMax) {
+                throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+                    "Chunk size exceeded max chunk size. Max: " + mChunkSizeMax
+                    + " Actual: " + mChunkLength);
+            }
+
+            if (mChunkLength >= mChunkSizeThreshold) {
+                OperationResult opResult = mKeyStoreStream.update(
+                        ArrayUtils.subarray(mChunk, 0, mChunkLength));
+
+                if (opResult == null) {
+                    throw new KeyStoreConnectException();
+                } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+                    throw KeyStore.getKeyStoreException(opResult.resultCode);
                 }
-            }
-            // Update input array references to reflect that some of its bytes are now in mBuffered.
-            inputOffset += inputBytesInChunk;
-            inputLength -= inputBytesInChunk;
-            mConsumedInputSizeBytes += inputBytesInChunk;
-
-            OperationResult opResult = mKeyStoreStream.update(chunk);
-            if (opResult == null) {
-                throw new KeyStoreConnectException();
-            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-                throw KeyStore.getKeyStoreException(opResult.resultCode);
-            }
-
-            if (opResult.inputConsumed == chunk.length) {
-                // The whole chunk was consumed
-                mBuffered = EmptyArray.BYTE;
-                mBufferedOffset = 0;
-                mBufferedLength = 0;
-            } else if (opResult.inputConsumed <= 0) {
-                // Nothing was consumed. More input needed.
-                if (inputLength > 0) {
-                    // More input is available, but it wasn't included into the previous chunk
-                    // because the chunk reached its maximum permitted size.
-                    // Shouldn't have happened.
+                if (opResult.inputConsumed <= 0) {
+                    throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+                        "Keystore consumed 0 of " + mChunkLength + " bytes provided.");
+                } else if (opResult.inputConsumed > mChunkLength) {
                     throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
-                            "Keystore consumed nothing from max-sized chunk: " + chunk.length
-                                    + " bytes");
+                        "Keystore consumed more input than provided. Provided: "
+                            + mChunkLength + ", consumed: " + opResult.inputConsumed);
                 }
-                mBuffered = chunk;
-                mBufferedOffset = 0;
-                mBufferedLength = chunk.length;
-            } else if (opResult.inputConsumed < chunk.length) {
-                // The chunk was consumed only partially -- buffer the rest of the chunk
-                mBuffered = chunk;
-                mBufferedOffset = opResult.inputConsumed;
-                mBufferedLength = chunk.length - opResult.inputConsumed;
-            } else {
-                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
-                        "Keystore consumed more input than provided. Provided: " + chunk.length
-                                + ", consumed: " + opResult.inputConsumed);
-            }
+                mChunkLength -= opResult.inputConsumed;
 
-            if ((opResult.output != null) && (opResult.output.length > 0)) {
-                if (inputLength + mBufferedLength > 0) {
-                    // More output might be produced in this loop -- buffer the current output
-                    if (bufferedOutput == null) {
-                        bufferedOutput = new ByteArrayOutputStream();
-                    }
-                    try {
-                        bufferedOutput.write(opResult.output);
-                    } catch (IOException e) {
-                        throw new ProviderException("Failed to buffer output", e);
-                    }
-                } else {
-                    // No more output will be produced in this loop
-                    byte[] result;
-                    if (bufferedOutput == null) {
-                        // No previously buffered output
-                        result = opResult.output;
-                    } else {
-                        // There was some previously buffered output
-                        try {
-                            bufferedOutput.write(opResult.output);
-                        } catch (IOException e) {
-                            throw new ProviderException("Failed to buffer output", e);
-                        }
-                        result = bufferedOutput.toByteArray();
-                    }
-                    mProducedOutputSizeBytes += result.length;
-                    return result;
+                if (mChunkLength > 0) {
+                    // Partialy consumed, shift chunk contents
+                    ArrayUtils.copy(mChunk, opResult.inputConsumed, mChunk, 0, mChunkLength);
+                }
+
+                if ((opResult.output != null) && (opResult.output.length > 0)) {
+                    // Output was produced
+                    mProducedOutputSizeBytes += opResult.output.length;
+                    output = ArrayUtils.concat(output, opResult.output);
                 }
             }
         }
-
-        byte[] result;
-        if (bufferedOutput == null) {
-            // No output produced
-            result = EmptyArray.BYTE;
-        } else {
-            result = bufferedOutput.toByteArray();
-        }
-        mProducedOutputSizeBytes += result.length;
-        return result;
+        return output;
     }
 
-    @Override
     public byte[] doFinal(byte[] input, int inputOffset, int inputLength,
             byte[] signature, byte[] additionalEntropy) throws KeyStoreException {
-        if (inputLength == 0) {
-            // No input provided -- simplify the rest of the code
-            input = EmptyArray.BYTE;
-            inputOffset = 0;
-        }
-
-        // Flush all buffered input and provided input into keystore/keymaster.
         byte[] output = update(input, inputOffset, inputLength);
-        output = ArrayUtils.concat(output, flush());
+        byte[] finalChunk = ArrayUtils.subarray(mChunk, 0, mChunkLength);
+        OperationResult opResult = mKeyStoreStream.finish(finalChunk, signature, additionalEntropy);
 
-        OperationResult opResult = mKeyStoreStream.finish(EmptyArray.BYTE, signature,
-                                                          additionalEntropy);
         if (opResult == null) {
             throw new KeyStoreConnectException();
         } else if (opResult.resultCode != KeyStore.NO_ERROR) {
             throw KeyStore.getKeyStoreException(opResult.resultCode);
         }
-        mProducedOutputSizeBytes += opResult.output.length;
+        // If no error, assume all input consumed
+        mConsumedInputSizeBytes += finalChunk.length;
 
-        return ArrayUtils.concat(output, opResult.output);
-    }
-
-    public byte[] flush() throws KeyStoreException {
-        if (mBufferedLength <= 0) {
-            return EmptyArray.BYTE;
+        if ((opResult.output != null) && (opResult.output.length > 0)) {
+            mProducedOutputSizeBytes += opResult.output.length;
+            output = ArrayUtils.concat(output, opResult.output);
         }
 
-        // Keep invoking the update operation with remaining buffered data until either all of the
-        // buffered data is consumed or until update fails to consume anything.
-        ByteArrayOutputStream bufferedOutput = null;
-        while (mBufferedLength > 0) {
-            byte[] chunk = ArrayUtils.subarray(mBuffered, mBufferedOffset, mBufferedLength);
-            OperationResult opResult = mKeyStoreStream.update(chunk);
-            if (opResult == null) {
-                throw new KeyStoreConnectException();
-            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
-                throw KeyStore.getKeyStoreException(opResult.resultCode);
-            }
-
-            if (opResult.inputConsumed <= 0) {
-                // Nothing was consumed. Break out of the loop to avoid an infinite loop.
-                break;
-            }
-
-            if (opResult.inputConsumed >= chunk.length) {
-                // All of the input was consumed
-                mBuffered = EmptyArray.BYTE;
-                mBufferedOffset = 0;
-                mBufferedLength = 0;
-            } else {
-                // Some of the input was not consumed
-                mBuffered = chunk;
-                mBufferedOffset = opResult.inputConsumed;
-                mBufferedLength = chunk.length - opResult.inputConsumed;
-            }
-
-            if (opResult.inputConsumed > chunk.length) {
-                throw new KeyStoreException(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
-                        "Keystore consumed more input than provided. Provided: "
-                                + chunk.length + ", consumed: " + opResult.inputConsumed);
-            }
-
-            if ((opResult.output != null) && (opResult.output.length > 0)) {
-                // Some output was produced by this update operation
-                if (bufferedOutput == null) {
-                    // No output buffered yet.
-                    if (mBufferedLength == 0) {
-                        // No more output will be produced by this flush operation
-                        mProducedOutputSizeBytes += opResult.output.length;
-                        return opResult.output;
-                    } else {
-                        // More output might be produced by this flush operation -- buffer output.
-                        bufferedOutput = new ByteArrayOutputStream();
-                    }
-                }
-                // Buffer the output from this update operation
-                try {
-                    bufferedOutput.write(opResult.output);
-                } catch (IOException e) {
-                    throw new ProviderException("Failed to buffer output", e);
-                }
-            }
-        }
-
-        if (mBufferedLength > 0) {
-            throw new KeyStoreException(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
-                    "Keystore failed to consume last "
-                            + ((mBufferedLength != 1) ? (mBufferedLength + " bytes") : "byte")
-                            + " of input");
-        }
-
-        byte[] result = (bufferedOutput != null) ? bufferedOutput.toByteArray() : EmptyArray.BYTE;
-        mProducedOutputSizeBytes += result.length;
-        return result;
+        return output;
     }
 
     @Override
diff --git a/media/Android.bp b/media/Android.bp
index a136517..499d0da 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -1,110 +1,3 @@
-java_library {
-    name: "updatable-media",
-
-    srcs: [
-        ":updatable-media-srcs",
-    ],
-
-    aidl: {
-        export_include_dirs: [
-            "apex/java",
-        ],
-
-        // It would be great if we don't need to add include_dirs for public
-        // parcelable classes. Find a better way.
-        include_dirs: [
-            // To refer:
-            // android.os.Bundle
-            // android.os.ResultReceiver
-            "frameworks/base/core/java",
-        ],
-    },
-
-    permitted_packages: [
-        "android.media",
-    ],
-
-    installable: true,
-
-    // TODO: build against stable API surface. Use core_platform for now to avoid
-    // link-check failure with exoplayer building against "current".
-    sdk_version: "core_platform",
-    libs: [
-        // The order matters. android_system_* library should come later.
-        "framework_media_annotation",
-        "android_system_stubs_current",
-    ],
-
-    static_libs: [
-        "exoplayer2-core"
-    ],
-    jarjar_rules: "jarjar_rules.txt",
-
-    plugins: ["java_api_finder"],
-}
-
-filegroup {
-    name: "updatable-media-srcs",
-    srcs: [
-        ":mediaparser-srcs",
-        ":mediasession2-srcs",
-    ],
-}
-
-filegroup {
-    name: "mediasession2-srcs",
-    srcs: [
-        "apex/java/android/media/Controller2Link.java",
-        "apex/java/android/media/IMediaController2.aidl",
-        "apex/java/android/media/IMediaSession2.aidl",
-        "apex/java/android/media/IMediaSession2Service.aidl",
-        "apex/java/android/media/MediaConstants.java",
-        "apex/java/android/media/MediaController2.java",
-        "apex/java/android/media/MediaSession2.java",
-        "apex/java/android/media/MediaSession2Service.java",
-        "apex/java/android/media/Session2Command.java",
-        "apex/java/android/media/Session2CommandGroup.java",
-        "apex/java/android/media/Session2Link.java",
-        "apex/java/android/media/Session2Token.java",
-    ],
-    path: "apex/java",
-}
-
-filegroup {
-    name: "mediaparser-srcs",
-    srcs: [
-        "apex/java/android/media/MediaParser.java"
-    ],
-    path: "apex/java"
-}
-
-droidstubs {
-    name: "updatable-media-stubs",
-    srcs: [
-        ":updatable-media-srcs",
-        ":framework-media-annotation-srcs",
-    ],
-    defaults: [ "framework-module-stubs-defaults-systemapi" ],
-    aidl: {
-        // TODO(b/135922046) remove this
-        include_dirs: ["frameworks/base/core/java"],
-    },
-    sdk_version: "system_current",
-}
-
-java_library {
-    name: "updatable_media_stubs",
-    srcs: [":updatable-media-stubs"],
-    sdk_version: "system_current",
-}
-
-java_library {
-    name: "framework_media_annotation",
-    srcs: [":framework-media-annotation-srcs"],
-    installable: false,
-    sdk_version: "core_current",
-}
-
 aidl_interface {
     name: "audio_common-aidl",
     local_include_dir: "java",
diff --git a/media/apex/java/android/media/CloseGuard.java b/media/apex/java/android/media/CloseGuard.java
deleted file mode 100644
index 2014673..0000000
--- a/media/apex/java/android/media/CloseGuard.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media;
-
-import android.util.Log;
-
-/**
- * Note: This file is copied from dalvik.system package with the following modifications:
- *       - Remove @CorePlatformApi, @IntraCoreApi and @UnsupportedAppUsage annotations.
- *       - Replace System.logW() with android.util.Log.w().
- *       This file should be used only within media mainline module.
- * TODO: Remove this file and use dalvik.system.CloseGuard once
- *       @CorePlatformApi becomes stable or we have a replacement in SDK API.
- *       b/120419300
- *
- * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
- * resources that should have been cleaned up by explicit close
- * methods (aka "explicit termination methods" in Effective Java).
- * <p>
- * A simple example: <pre>   {@code
- *   class Foo {
- *
- *       {@literal @}ReachabilitySensitive
- *       private final CloseGuard guard = CloseGuard.get();
- *
- *       ...
- *
- *       public Foo() {
- *           ...;
- *           guard.open("cleanup");
- *       }
- *
- *       public void cleanup() {
- *          guard.close();
- *          ...;
- *       }
- *
- *       protected void finalize() throws Throwable {
- *           try {
- *               // Note that guard could be null if the constructor threw.
- *               if (guard != null) {
- *                   guard.warnIfOpen();
- *               }
- *               cleanup();
- *           } finally {
- *               super.finalize();
- *           }
- *       }
- *   }
- * }</pre>
- *
- * In usage where the resource to be explicitly cleaned up is
- * allocated after object construction, CloseGuard protection can
- * be deferred. For example: <pre>   {@code
- *   class Bar {
- *
- *       {@literal @}ReachabilitySensitive
- *       private final CloseGuard guard = CloseGuard.get();
- *
- *       ...
- *
- *       public Bar() {
- *           ...;
- *       }
- *
- *       public void connect() {
- *          ...;
- *          guard.open("cleanup");
- *       }
- *
- *       public void cleanup() {
- *          guard.close();
- *          ...;
- *       }
- *
- *       protected void finalize() throws Throwable {
- *           try {
- *               // Note that guard could be null if the constructor threw.
- *               if (guard != null) {
- *                   guard.warnIfOpen();
- *               }
- *               cleanup();
- *           } finally {
- *               super.finalize();
- *           }
- *       }
- *   }
- * }</pre>
- *
- * When used in a constructor, calls to {@code open} should occur at
- * the end of the constructor since an exception that would cause
- * abrupt termination of the constructor will mean that the user will
- * not have a reference to the object to cleanup explicitly. When used
- * in a method, the call to {@code open} should occur just after
- * resource acquisition.
- *
- * The @ReachabilitySensitive annotation ensures that finalize() cannot be
- * called during the explicit call to cleanup(), prior to the guard.close call.
- * There is an extremely small chance that, for code that neglects to call
- * cleanup(), finalize() and thus cleanup() will be called while a method on
- * the object is still active, but the "this" reference is no longer required.
- * If missing cleanup() calls are expected, additional @ReachabilitySensitive
- * annotations or reachabilityFence() calls may be required.
- *
- * @hide
- */
-final class CloseGuard {
-
-    /**
-     * True if collection of call-site information (the expensive operation
-     * here)  and tracking via a Tracker (see below) are enabled.
-     * Enabled by default so we can diagnose issues early in VM startup.
-     * Note, however, that Android disables this early in its startup,
-     * but enables it with DropBoxing for system apps on debug builds.
-     */
-    private static volatile boolean stackAndTrackingEnabled = true;
-
-    /**
-     * Hook for customizing how CloseGuard issues are reported.
-     * Bypassed if stackAndTrackingEnabled was false when open was called.
-     */
-    private static volatile Reporter reporter = new DefaultReporter();
-
-    /**
-     * Hook for customizing how CloseGuard issues are tracked.
-     */
-    private static volatile Tracker currentTracker = null; // Disabled by default.
-
-    /**
-     * Returns a CloseGuard instance. {@code #open(String)} can be used to set
-     * up the instance to warn on failure to close.
-     */
-    public static CloseGuard get() {
-        return new CloseGuard();
-    }
-
-    /**
-     * Enables/disables stack capture and tracking. A call stack is captured
-     * during open(), and open/close events are reported to the Tracker, only
-     * if enabled is true. If a stack trace was captured, the {@link
-     * #getReporter() reporter} is informed of unclosed resources; otherwise a
-     * one-line warning is logged.
-     */
-    public static void setEnabled(boolean enabled) {
-        CloseGuard.stackAndTrackingEnabled = enabled;
-    }
-
-    /**
-     * True if CloseGuard stack capture and tracking are enabled.
-     */
-    public static boolean isEnabled() {
-        return stackAndTrackingEnabled;
-    }
-
-    /**
-     * Used to replace default Reporter used to warn of CloseGuard
-     * violations when stack tracking is enabled. Must be non-null.
-     */
-    public static void setReporter(Reporter rep) {
-        if (rep == null) {
-            throw new NullPointerException("reporter == null");
-        }
-        CloseGuard.reporter = rep;
-    }
-
-    /**
-     * Returns non-null CloseGuard.Reporter.
-     */
-    public static Reporter getReporter() {
-        return reporter;
-    }
-
-    /**
-     * Sets the {@link Tracker} that is notified when resources are allocated and released.
-     * The Tracker is invoked only if CloseGuard {@link #isEnabled()} held when {@link #open()}
-     * was called. A null argument disables tracking.
-     *
-     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
-     * MUST NOT be used for any other purposes.
-     */
-    public static void setTracker(Tracker tracker) {
-        currentTracker = tracker;
-    }
-
-    /**
-     * Returns {@link #setTracker(Tracker) last Tracker that was set}, or null to indicate
-     * there is none.
-     *
-     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
-     * MUST NOT be used for any other purposes.
-     */
-    public static Tracker getTracker() {
-        return currentTracker;
-    }
-
-    private CloseGuard() {}
-
-    /**
-     * {@code open} initializes the instance with a warning that the caller
-     * should have explicitly called the {@code closer} method instead of
-     * relying on finalization.
-     *
-     * @param closer non-null name of explicit termination method. Printed by warnIfOpen.
-     * @throws NullPointerException if closer is null.
-     */
-    public void open(String closer) {
-        // always perform the check for valid API usage...
-        if (closer == null) {
-            throw new NullPointerException("closer == null");
-        }
-        // ...but avoid allocating an allocation stack if "disabled"
-        if (!stackAndTrackingEnabled) {
-            closerNameOrAllocationInfo = closer;
-            return;
-        }
-        String message = "Explicit termination method '" + closer + "' not called";
-        Throwable stack = new Throwable(message);
-        closerNameOrAllocationInfo = stack;
-        Tracker tracker = currentTracker;
-        if (tracker != null) {
-            tracker.open(stack);
-        }
-    }
-
-    // We keep either an allocation stack containing the closer String or, when
-    // in disabled state, just the closer String.
-    // We keep them in a single field only to minimize overhead.
-    private Object /* String or Throwable */ closerNameOrAllocationInfo;
-
-    /**
-     * Marks this CloseGuard instance as closed to avoid warnings on
-     * finalization.
-     */
-    public void close() {
-        Tracker tracker = currentTracker;
-        if (tracker != null && closerNameOrAllocationInfo instanceof Throwable) {
-            // Invoke tracker on close only if we invoked it on open. Tracker may have changed.
-            tracker.close((Throwable) closerNameOrAllocationInfo);
-        }
-        closerNameOrAllocationInfo = null;
-    }
-
-    /**
-     * Logs a warning if the caller did not properly cleanup by calling an
-     * explicit close method before finalization. If CloseGuard was enabled
-     * when the CloseGuard was created, passes the stacktrace associated with
-     * the allocation to the current reporter. If it was not enabled, it just
-     * directly logs a brief message.
-     */
-    public void warnIfOpen() {
-        if (closerNameOrAllocationInfo != null) {
-            if (closerNameOrAllocationInfo instanceof String) {
-                Log.w("CloseGuard", "A resource failed to call "
-                        + (String) closerNameOrAllocationInfo + ". ");
-            } else {
-                String message =
-                        "A resource was acquired at attached stack trace but never released. ";
-                message += "See java.io.Closeable for information on avoiding resource leaks.";
-                Throwable stack = (Throwable) closerNameOrAllocationInfo;
-                reporter.report(message, stack);
-            }
-        }
-    }
-
-    /**
-     * Interface to allow customization of tracking behaviour.
-     *
-     * <p>This is only intended for use by {@code dalvik.system.CloseGuardSupport} class and so
-     * MUST NOT be used for any other purposes.
-     */
-    public interface Tracker {
-        void open(Throwable allocationSite);
-        void close(Throwable allocationSite);
-    }
-
-    /**
-     * Interface to allow customization of reporting behavior.
-     * @hide
-     */
-    public interface Reporter {
-        void report(String message, Throwable allocationSite);
-    }
-
-    /**
-     * Default Reporter which reports CloseGuard violations to the log.
-     */
-    private static final class DefaultReporter implements Reporter {
-        private DefaultReporter() {}
-
-        @Override public void report (String message, Throwable allocationSite) {
-            Log.w("CloseGuard", message, allocationSite);
-        }
-    }
-}
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 5f32c0f..f3613d3 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.media.audiofx.AudioEffect;
@@ -224,15 +225,16 @@
     public String getClientPackageName() { return mClientPackageName; }
 
     /**
-     * @pending for SystemApi
      * Returns the user id of the application performing the recording.
      * <p>This information is only available if the caller has the
      * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING}
      * permission.
      * <br>The result is -1 without the permission.
      * @return the user id
+     *
+     * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public int getClientUid() { return mClientUid; }
 
     /**
diff --git a/media/java/android/media/IMediaRouter2Client.aidl b/media/java/android/media/IMediaRouter2Client.aidl
index 18a6428..f90c7c5 100644
--- a/media/java/android/media/IMediaRouter2Client.aidl
+++ b/media/java/android/media/IMediaRouter2Client.aidl
@@ -30,4 +30,5 @@
     void notifyRoutesChanged(in List<MediaRoute2Info> routes);
     void notifySessionCreated(in @nullable RouteSessionInfo sessionInfo, int requestId);
     void notifySessionInfoChanged(in RouteSessionInfo sessionInfo);
+    void notifySessionReleased(in RouteSessionInfo sessionInfo);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 4b7d802..e5b62ff 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -56,6 +56,7 @@
     void selectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void deselectRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
     void transferToRoute(IMediaRouter2Client client, String sessionId, in MediaRoute2Info route);
+    void releaseSession(IMediaRouter2Client client, String sessionId);
 
     void registerManager(IMediaRouter2Manager manager, String packageName);
     void unregisterManager(IMediaRouter2Manager manager);
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 176bb37..f780d40 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -362,7 +362,8 @@
    </tr>
    <tr>
     <td>FLAC</td>
-    <td>mandatory metadata block (called the STREAMINFO block),<br>
+    <td>"fLaC", the FLAC stream marker in ASCII,<br>
+        followed by the STREAMINFO block (the mandatory metadata block),<br>
         optionally followed by any number of other metadata blocks</td>
     <td class=NA>Not Used</td>
     <td class=NA>Not Used</td>
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 0be49d8..f5cfde4b 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -96,7 +96,7 @@
 
     private static final String TAG = "MR2";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final Object sLock = new Object();
+    private static final Object sRouterLock = new Object();
 
     @GuardedBy("sLock")
     private static MediaRouter2 sInstance;
@@ -122,8 +122,9 @@
 
     // TODO: Make MediaRouter2 is always connected to the MediaRouterService.
     @GuardedBy("sLock")
-    private Client2 mClient;
+    Client2 mClient;
 
+    @GuardedBy("sLock")
     private Map<String, RouteSessionController> mSessionControllers = new ArrayMap<>();
 
     private AtomicInteger mSessionCreationRequestCnt = new AtomicInteger(1);
@@ -138,7 +139,7 @@
      */
     public static MediaRouter2 getInstance(@NonNull Context context) {
         Objects.requireNonNull(context, "context must not be null");
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             if (sInstance == null) {
                 sInstance = new MediaRouter2(context.getApplicationContext());
             }
@@ -210,7 +211,7 @@
             return;
         }
 
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             if (mClient == null) {
                 Client2 client = new Client2();
                 try {
@@ -242,7 +243,7 @@
             return;
         }
 
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             if (mRouteCallbackRecords.size() == 0 && mClient != null) {
                 try {
                     mMediaRouterService.unregisterClient2(mClient);
@@ -266,7 +267,7 @@
 
         List<String> newControlCategories = new ArrayList<>(controlCategories);
 
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             mShouldUpdateRoutes = true;
 
             // invoke callbacks due to control categories change
@@ -291,7 +292,7 @@
      */
     @NonNull
     public List<MediaRoute2Info> getRoutes() {
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             if (mShouldUpdateRoutes) {
                 mShouldUpdateRoutes = false;
 
@@ -372,7 +373,7 @@
         mSessionCreationRequests.add(request);
 
         Client2 client;
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             client = mClient;
         }
         if (client != null) {
@@ -400,7 +401,7 @@
         Objects.requireNonNull(request, "request must not be null");
 
         Client2 client;
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             client = mClient;
         }
         if (client != null) {
@@ -424,7 +425,7 @@
         Objects.requireNonNull(route, "route must not be null");
 
         Client2 client;
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             client = mClient;
         }
         if (client != null) {
@@ -448,7 +449,7 @@
         Objects.requireNonNull(route, "route must not be null");
 
         Client2 client;
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             client = mClient;
         }
         if (client != null) {
@@ -496,7 +497,7 @@
         //  2) Call onRouteSelected(system_route, reason_fallback) if previously selected route
         //     does not exist anymore. => We may need 'boolean MediaRoute2Info#isSystemRoute()'.
         List<MediaRoute2Info> addedRoutes = new ArrayList<>();
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getUniqueId(), route);
                 if (route.supportsControlCategories(mControlCategories)) {
@@ -512,7 +513,7 @@
 
     void removeRoutesOnHandler(List<MediaRoute2Info> routes) {
         List<MediaRoute2Info> removedRoutes = new ArrayList<>();
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.remove(route.getUniqueId());
                 if (route.supportsControlCategories(mControlCategories)) {
@@ -528,7 +529,7 @@
 
     void changeRoutesOnHandler(List<MediaRoute2Info> routes) {
         List<MediaRoute2Info> changedRoutes = new ArrayList<>();
-        synchronized (sLock) {
+        synchronized (sRouterLock) {
             for (MediaRoute2Info route : routes) {
                 mRoutes.put(route.getUniqueId(), route);
                 if (route.supportsControlCategories(mControlCategories)) {
@@ -547,8 +548,6 @@
      * {@link SessionCallback#onSessionCreationFailed}.
      * <p>
      * Pass {@code null} to sessionInfo for the failure case.
-     *
-     * TODO: What should router do when the session is created by manager?
      */
     void createControllerOnHandler(@Nullable RouteSessionInfo sessionInfo, int requestId) {
         SessionCreationRequest matchingRequest = null;
@@ -559,42 +558,48 @@
             }
         }
 
-        if (matchingRequest == null) {
-            Log.w(TAG, "Ignoring session creation result for unknown request."
-                    + " requestId=" + requestId + ", sessionInfo=" + sessionInfo);
-            return;
+        if (matchingRequest != null) {
+            mSessionCreationRequests.remove(matchingRequest);
+
+            MediaRoute2Info requestedRoute = matchingRequest.mRoute;
+            String requestedControlCategory = matchingRequest.mControlCategory;
+
+            if (sessionInfo == null) {
+                // TODO: We may need to distinguish between failure and rejection.
+                //       One way can be introducing 'reason'.
+                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                return;
+            } else if (!TextUtils.equals(requestedControlCategory,
+                    sessionInfo.getControlCategory())) {
+                Log.w(TAG, "The session has different control category from what we requested. "
+                        + "(requested=" + requestedControlCategory
+                        + ", actual=" + sessionInfo.getControlCategory()
+                        + ")");
+                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                return;
+            } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
+                Log.w(TAG, "The session does not contain the requested route. "
+                        + "(requestedRouteId=" + requestedRoute.getId()
+                        + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
+                        + ")");
+                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                return;
+            } else if (!TextUtils.equals(requestedRoute.getProviderId(),
+                    sessionInfo.getProviderId())) {
+                Log.w(TAG, "The session's provider ID does not match the requested route's. "
+                        + "(requested route's providerId=" + requestedRoute.getProviderId()
+                        + ", actual providerId=" + sessionInfo.getProviderId()
+                        + ")");
+                notifySessionCreationFailed(requestedRoute, requestedControlCategory);
+                return;
+            }
         }
 
-        mSessionCreationRequests.remove(matchingRequest);
-
-        MediaRoute2Info requestedRoute = matchingRequest.mRoute;
-        String requestedControlCategory = matchingRequest.mControlCategory;
-
-        if (sessionInfo == null) {
-            // TODO: We may need to distinguish between failure and rejection.
-            //       One way can be introducing 'reason'.
-            notifySessionCreationFailed(requestedRoute, requestedControlCategory);
-        } else if (!TextUtils.equals(requestedControlCategory, sessionInfo.getControlCategory())) {
-            Log.w(TAG, "The session has different control category from what we requested. "
-                    + "(requested=" + requestedControlCategory
-                    + ", actual=" + sessionInfo.getControlCategory()
-                    + ")");
-            notifySessionCreationFailed(requestedRoute, requestedControlCategory);
-        } else if (!sessionInfo.getSelectedRoutes().contains(requestedRoute.getId())) {
-            Log.w(TAG, "The session does not contain the requested route. "
-                    + "(requestedRouteId=" + requestedRoute.getId()
-                    + ", actualRoutes=" + sessionInfo.getSelectedRoutes()
-                    + ")");
-            notifySessionCreationFailed(requestedRoute, requestedControlCategory);
-        } else if (!TextUtils.equals(requestedRoute.getProviderId(), sessionInfo.getProviderId())) {
-            Log.w(TAG, "The session's provider ID does not match the requested route's. "
-                    + "(requested route's providerId=" + requestedRoute.getProviderId()
-                    + ", actual providerId=" + sessionInfo.getProviderId()
-                    + ")");
-            notifySessionCreationFailed(requestedRoute, requestedControlCategory);
-        } else {
+        if (sessionInfo != null) {
             RouteSessionController controller = new RouteSessionController(sessionInfo);
-            mSessionControllers.put(controller.getUniqueSessionId(), controller);
+            synchronized (sRouterLock) {
+                mSessionControllers.put(controller.getUniqueSessionId(), controller);
+            }
             notifySessionCreated(controller);
         }
     }
@@ -605,8 +610,10 @@
             return;
         }
 
-        RouteSessionController matchingController = mSessionControllers.get(
-                sessionInfo.getUniqueSessionId());
+        RouteSessionController matchingController;
+        synchronized (sRouterLock) {
+            matchingController = mSessionControllers.get(sessionInfo.getUniqueSessionId());
+        }
 
         if (matchingController == null) {
             Log.w(TAG, "changeSessionInfoOnHandler: Matching controller not found. uniqueSessionId="
@@ -625,6 +632,40 @@
         notifySessionInfoChanged(matchingController, oldInfo, sessionInfo);
     }
 
+    void releaseControllerOnHandler(RouteSessionInfo sessionInfo) {
+        if (sessionInfo == null) {
+            Log.w(TAG, "releaseControllerOnHandler: Ignoring null sessionInfo.");
+            return;
+        }
+
+        final String uniqueSessionId = sessionInfo.getUniqueSessionId();
+        RouteSessionController matchingController;
+        synchronized (sRouterLock) {
+            matchingController = mSessionControllers.get(uniqueSessionId);
+        }
+
+        if (matchingController == null) {
+            if (DEBUG) {
+                Log.d(TAG, "releaseControllerOnHandler: Matching controller not found. "
+                        + "uniqueSessionId=" + sessionInfo.getUniqueSessionId());
+            }
+            return;
+        }
+
+        RouteSessionInfo oldInfo = matchingController.getRouteSessionInfo();
+        if (!TextUtils.equals(oldInfo.getProviderId(), sessionInfo.getProviderId())) {
+            Log.w(TAG, "releaseControllerOnHandler: Provider IDs are not matched. old="
+                    + oldInfo.getProviderId() + ", new=" + sessionInfo.getProviderId());
+            return;
+        }
+
+        synchronized (sRouterLock) {
+            mSessionControllers.remove(uniqueSessionId, matchingController);
+        }
+        matchingController.release();
+        notifyControllerReleased(matchingController);
+    }
+
     private void notifyRoutesAdded(List<MediaRoute2Info> routes) {
         for (RouteCallbackRecord record: mRouteCallbackRecords) {
             record.mExecutor.execute(
@@ -669,6 +710,13 @@
         }
     }
 
+    private void notifyControllerReleased(RouteSessionController controller) {
+        for (SessionCallbackRecord record: mSessionCallbackRecords) {
+            record.mExecutor.execute(
+                    () -> record.mSessionCallback.onSessionReleased(controller));
+        }
+    }
+
     /**
      * Callback for receiving events about media route discovery.
      */
@@ -735,14 +783,20 @@
                 @NonNull RouteSessionInfo newInfo) {}
 
         /**
-         * Called when the session is released. Session can be released by the controller using
-         * {@link RouteSessionController#release(boolean)}, or by the
-         * {@link MediaRoute2ProviderService} itself. One can do clean-ups here.
+         * Called when the session is released by {@link MediaRoute2ProviderService}.
+         * Before this method is called, the controller would be released by the system,
+         * which means the {@link RouteSessionController#isReleased()} will always return true
+         * for the {@code controller} here.
+         * <p>
+         * Note: Calling {@link RouteSessionController#release()} will <em>NOT</em> trigger
+         * this method to be called.
          *
-         * TODO: When Provider#notifySessionDestroyed is introduced, add @see for the method.
+         * TODO: Add tests for checking whether this method is called.
+         * TODO: When service process dies, this should be called.
+         *
+         * @see RouteSessionController#isReleased()
          */
-        public void onSessionReleased(@NonNull RouteSessionController controller, int reason,
-                boolean shouldStop) {}
+        public void onSessionReleased(@NonNull RouteSessionController controller) {}
     }
 
     /**
@@ -753,7 +807,7 @@
      * TODO: Need to add toString()
      */
     public final class RouteSessionController {
-        private final Object mLock = new Object();
+        private final Object mControllerLock = new Object();
 
         @GuardedBy("mLock")
         private RouteSessionInfo mSessionInfo;
@@ -769,7 +823,7 @@
          * @return the ID of the session
          */
         public int getSessionId() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getSessionId();
             }
         }
@@ -780,7 +834,7 @@
          */
         @NonNull
         public String getUniqueSessionId() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getUniqueSessionId();
             }
         }
@@ -790,7 +844,7 @@
          */
         @NonNull
         public String getControlCategory() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getControlCategory();
             }
         }
@@ -800,7 +854,7 @@
          */
         @Nullable
         public Bundle getControlHints() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getControlHints();
             }
         }
@@ -810,7 +864,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getSelectedRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getSelectedRoutes());
             }
         }
@@ -820,7 +874,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getSelectableRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getSelectableRoutes());
             }
         }
@@ -830,7 +884,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getDeselectableRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getDeselectableRoutes());
             }
         }
@@ -840,7 +894,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getTransferrableRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getTransferrableRoutes());
             }
         }
@@ -851,10 +905,9 @@
          * Also, any operations to this instance will be ignored once released.
          *
          * @see #release
-         * @see SessionCallback#onSessionReleased
          */
         public boolean isReleased() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mIsReleased;
             }
         }
@@ -874,6 +927,12 @@
          */
         public void selectRoute(@NonNull MediaRoute2Info route) {
             Objects.requireNonNull(route, "route must not be null");
+            synchronized (mControllerLock) {
+                if (mIsReleased) {
+                    Log.w(TAG, "selectRoute() called on released controller. Ignoring.");
+                    return;
+                }
+            }
 
             List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
             if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
@@ -888,12 +947,12 @@
             }
 
             Client2 client;
-            synchronized (sLock) {
+            synchronized (sRouterLock) {
                 client = mClient;
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.selectRoute(mClient, getUniqueSessionId(), route);
+                    mMediaRouterService.selectRoute(client, getUniqueSessionId(), route);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to select route for session.", ex);
                 }
@@ -915,6 +974,12 @@
          */
         public void deselectRoute(@NonNull MediaRoute2Info route) {
             Objects.requireNonNull(route, "route must not be null");
+            synchronized (mControllerLock) {
+                if (mIsReleased) {
+                    Log.w(TAG, "deselectRoute() called on released controller. Ignoring.");
+                    return;
+                }
+            }
 
             List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
             if (!checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
@@ -929,12 +994,12 @@
             }
 
             Client2 client;
-            synchronized (sLock) {
+            synchronized (sRouterLock) {
                 client = mClient;
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.deselectRoute(mClient, getUniqueSessionId(), route);
+                    mMediaRouterService.deselectRoute(client, getUniqueSessionId(), route);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to remove route from session.", ex);
                 }
@@ -956,6 +1021,12 @@
          */
         public void transferToRoute(@NonNull MediaRoute2Info route) {
             Objects.requireNonNull(route, "route must not be null");
+            synchronized (mControllerLock) {
+                if (mIsReleased) {
+                    Log.w(TAG, "transferToRoute() called on released controller. Ignoring.");
+                    return;
+                }
+            }
 
             List<MediaRoute2Info> selectedRoutes = getSelectedRoutes();
             if (checkRouteListContainsRouteId(selectedRoutes, route.getUniqueId())) {
@@ -971,12 +1042,12 @@
             }
 
             Client2 client;
-            synchronized (sLock) {
+            synchronized (sRouterLock) {
                 client = mClient;
             }
             if (client != null) {
                 try {
-                    mMediaRouterService.transferToRoute(mClient, getUniqueSessionId(), route);
+                    mMediaRouterService.transferToRoute(client, getUniqueSessionId(), route);
                 } catch (RemoteException ex) {
                     Log.e(TAG, "Unable to transfer to route for session.", ex);
                 }
@@ -984,45 +1055,53 @@
         }
 
         /**
-         * Release this session.
-         * Any operation on this session after calling this method will be ignored.
+         * Release this controller and corresponding session.
+         * Any operations on this controller after calling this method will be ignored.
+         * The devices that are playing media will stop playing it.
          *
-         * @param stopMedia Should the media that is playing on the device be stopped after this
-         *                  session is released.
-         * @see SessionCallback#onSessionReleased
+         * TODO: Add tests using {@link MediaRouter2Manager#getActiveSessions()}.
          */
-        public void release(boolean stopMedia) {
-            synchronized (mLock) {
+        public void release() {
+            synchronized (mControllerLock) {
                 if (mIsReleased) {
+                    Log.w(TAG, "release() called on released controller. Ignoring.");
                     return;
                 }
                 mIsReleased = true;
             }
-            // TODO: Use stopMedia variable when the actual connection logic is implemented.
+
+            Client2 client;
+            synchronized (sRouterLock) {
+                mSessionControllers.remove(getUniqueSessionId(), this);
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.releaseSession(client, getUniqueSessionId());
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "Unable to notify of controller release", ex);
+                }
+            }
         }
 
-        /**
-         * @hide
-         */
         @NonNull
-        public RouteSessionInfo getRouteSessionInfo() {
-            synchronized (mLock) {
+        RouteSessionInfo getRouteSessionInfo() {
+            synchronized (mControllerLock) {
                 return mSessionInfo;
             }
         }
 
-        /**
-         * @hide
-         */
-        public void setRouteSessionInfo(@NonNull RouteSessionInfo info) {
-            synchronized (mLock) {
+        void setRouteSessionInfo(@NonNull RouteSessionInfo info) {
+            synchronized (mControllerLock) {
                 mSessionInfo = info;
             }
         }
 
+        // TODO: This method uses two locks (mLock outside, sLock inside).
+        //       Check if there is any possiblity of deadlock.
         private List<MediaRoute2Info> getRoutesWithIdsLocked(List<String> routeIds) {
             List<MediaRoute2Info> routes = new ArrayList<>();
-            synchronized (mLock) {
+            synchronized (sRouterLock) {
                 for (String routeId : routeIds) {
                     MediaRoute2Info route = mRoutes.get(
                             MediaRoute2Info.toUniqueId(mSessionInfo.mProviderId, routeId));
@@ -1137,5 +1216,11 @@
             mHandler.sendMessage(obtainMessage(MediaRouter2::changeSessionInfoOnHandler,
                     MediaRouter2.this, sessionInfo));
         }
+
+        @Override
+        public void notifySessionReleased(RouteSessionInfo sessionInfo) {
+            mHandler.sendMessage(obtainMessage(MediaRouter2::releaseControllerOnHandler,
+                    MediaRouter2.this, sessionInfo));
+        }
     }
 }
diff --git a/media/java/android/media/RouteSessionInfo.java b/media/java/android/media/RouteSessionInfo.java
index b9cf15e..2d7bc24 100644
--- a/media/java/android/media/RouteSessionInfo.java
+++ b/media/java/android/media/RouteSessionInfo.java
@@ -106,9 +106,47 @@
 
     /**
      * Gets non-unique session id (int) from unique session id (string).
+     * If the corresponding session id could not be generated, it will return null.
+     * @hide
      */
-    public static int getSessionId(@NonNull String uniqueSessionId, @NonNull String providerId) {
-        return Integer.parseInt(uniqueSessionId.substring(providerId.length() + 1));
+    @Nullable
+    public static Integer getSessionId(@NonNull String uniqueSessionId) {
+        int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
+        if (lastIndexOfSeparator == -1 || lastIndexOfSeparator + 1 >= uniqueSessionId.length()) {
+            return null;
+        }
+
+        String integerString = uniqueSessionId.substring(lastIndexOfSeparator + 1);
+        if (TextUtils.isEmpty(integerString)) {
+            return null;
+        }
+
+        try {
+            return Integer.parseInt(integerString);
+        } catch (NumberFormatException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets provider ID (string) from unique session id (string).
+     * If the corresponding provider ID could not be generated, it will return null.
+     * @hide
+     *
+     * TODO: This logic seems error-prone. Consider to use long uniqueId.
+     */
+    @Nullable
+    public static String getProviderId(@NonNull String uniqueSessionId) {
+        int lastIndexOfSeparator = uniqueSessionId.lastIndexOf("/");
+        if (lastIndexOfSeparator == -1) {
+            return null;
+        }
+
+        String result = uniqueSessionId.substring(0, lastIndexOfSeparator);
+        if (TextUtils.isEmpty(result)) {
+            return null;
+        }
+        return result;
     }
 
     /**
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 4a40c62..cad5aa6 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -16,11 +16,18 @@
 
 package android.media.audiofx;
 
+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;
 import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
+import android.media.AudioSystem;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -448,12 +455,46 @@
     public AudioEffect(UUID type, UUID uuid, int priority, int audioSession)
             throws IllegalArgumentException, UnsupportedOperationException,
             RuntimeException {
+        this(type, uuid, priority, audioSession, null);
+    }
+
+    /**
+     * Constructs an AudioEffect attached to a particular audio device.
+     * The device does not have to be attached when the effect is created. The effect will only
+     * be applied when the device is actually selected for playback or capture.
+     * @param uuid unique identifier of a particular effect implementation.
+     * @param device the device the effect must be attached to.
+     *
+     * @throws java.lang.IllegalArgumentException
+     * @throws java.lang.UnsupportedOperationException
+     * @throws java.lang.RuntimeException
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    public AudioEffect(@NonNull UUID uuid, @NonNull AudioDeviceAddress 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)
+            throws IllegalArgumentException, UnsupportedOperationException,
+            RuntimeException {
         int[] id = new int[1];
         Descriptor[] desc = new Descriptor[1];
+
+        int deviceType = AudioSystem.DEVICE_NONE;
+        String deviceAddress = "";
+        if (device != null) {
+            deviceType = AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
+            deviceAddress = device.getAddress();
+        }
+
         // native initialization
         int initResult = native_setup(new WeakReference<AudioEffect>(this),
-                type.toString(), uuid.toString(), priority, audioSession, id,
-                desc, ActivityThread.currentOpPackageName());
+                type.toString(), uuid.toString(), priority, audioSession,
+                deviceType, deviceAddress,
+                id, desc, ActivityThread.currentOpPackageName());
         if (initResult != SUCCESS && initResult != ALREADY_EXISTS) {
             Log.e(TAG, "Error code " + initResult
                     + " when initializing AudioEffect.");
@@ -1293,7 +1334,8 @@
     private static native final void native_init();
 
     private native final int native_setup(Object audioeffect_this, String type,
-            String uuid, int priority, int audioSession, int[] id, Object[] desc,
+            String uuid, int priority, int audioSession,
+            int deviceType, String deviceAddress, int[] id, Object[] desc,
             String opPackageName);
 
     private native final void native_finalize();
diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
index a574fe1..96c8528 100644
--- a/media/java/android/media/tv/DvbDeviceInfo.java
+++ b/media/java/android/media/tv/DvbDeviceInfo.java
@@ -16,6 +16,8 @@
 
 package android.media.tv;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -26,10 +28,11 @@
  *
  * @hide
  */
+@SystemApi
 public final class DvbDeviceInfo implements Parcelable {
     static final String TAG = "DvbDeviceInfo";
 
-    public static final @android.annotation.NonNull Parcelable.Creator<DvbDeviceInfo> CREATOR =
+    public static final @NonNull Parcelable.Creator<DvbDeviceInfo> CREATOR =
             new Parcelable.Creator<DvbDeviceInfo>() {
                 @Override
                 public DvbDeviceInfo createFromParcel(Parcel source) {
@@ -86,7 +89,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mAdapterId);
         dest.writeInt(mDeviceId);
     }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index d22a298..854ea43 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -103,6 +103,12 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DVB_DEVICE_DEMUX, DVB_DEVICE_DVR, DVB_DEVICE_FRONTEND})
+    public @interface DvbDeviceType {}
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
     @IntDef({VIDEO_UNAVAILABLE_REASON_UNKNOWN, VIDEO_UNAVAILABLE_REASON_TUNING,
             VIDEO_UNAVAILABLE_REASON_WEAK_SIGNAL, VIDEO_UNAVAILABLE_REASON_BUFFERING,
             VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY})
@@ -1663,6 +1669,9 @@
      * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.DVB_DEVICE)
+    @NonNull
     public List<DvbDeviceInfo> getDvbDeviceList() {
         try {
             return mService.getDvbDeviceList();
@@ -1676,19 +1685,24 @@
      * {@link DvbDeviceInfo}
      *
      * @param info A {@link DvbDeviceInfo} to open a DVB device.
-     * @param device A DVB device. The DVB device can be {@link #DVB_DEVICE_DEMUX},
+     * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX},
      *            {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}.
      * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
-     *         {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} was invalid
-     *         or the specified DVB device was busy with a previous request.
+     *         {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
+     *         failed to open.
+     * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found.
      * @hide
      */
-    public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device) {
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.DVB_DEVICE)
+    @Nullable
+    public ParcelFileDescriptor openDvbDevice(@NonNull DvbDeviceInfo info,
+            @DvbDeviceType int deviceType) {
         try {
-            if (DVB_DEVICE_START > device || DVB_DEVICE_END < device) {
-                throw new IllegalArgumentException("Invalid DVB device: " + device);
+            if (DVB_DEVICE_START > deviceType || DVB_DEVICE_END < deviceType) {
+                throw new IllegalArgumentException("Invalid DVB device: " + deviceType);
             }
-            return mService.openDvbDevice(info, device);
+            return mService.openDvbDevice(info, deviceType);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index ea00d6e..ab7bbca 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -61,6 +61,7 @@
     private final boolean mEncrypted;
     private final int mAudioChannelCount;
     private final int mAudioSampleRate;
+    private final boolean mAudioDescription;
     private final int mVideoWidth;
     private final int mVideoHeight;
     private final float mVideoFrameRate;
@@ -70,8 +71,8 @@
     private final Bundle mExtra;
 
     private TvTrackInfo(int type, String id, String language, CharSequence description,
-            boolean encrypted, int audioChannelCount, int audioSampleRate, int videoWidth,
-            int videoHeight, float videoFrameRate, float videoPixelAspectRatio,
+            boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription,
+            int videoWidth, int videoHeight, float videoFrameRate, float videoPixelAspectRatio,
             byte videoActiveFormatDescription, Bundle extra) {
         mType = type;
         mId = id;
@@ -80,6 +81,7 @@
         mEncrypted = encrypted;
         mAudioChannelCount = audioChannelCount;
         mAudioSampleRate = audioSampleRate;
+        mAudioDescription = audioDescription;
         mVideoWidth = videoWidth;
         mVideoHeight = videoHeight;
         mVideoFrameRate = videoFrameRate;
@@ -96,6 +98,7 @@
         mEncrypted = in.readInt() != 0;
         mAudioChannelCount = in.readInt();
         mAudioSampleRate = in.readInt();
+        mAudioDescription = in.readInt() != 0;
         mVideoWidth = in.readInt();
         mVideoHeight = in.readInt();
         mVideoFrameRate = in.readFloat();
@@ -172,6 +175,23 @@
     }
 
     /**
+     * Returns {@code true} if the track is an audio description intended for people with visual
+     * impairment, {@code false} otherwise. Valid only for {@link #TYPE_AUDIO} tracks.
+     *
+     * <p>For example of broadcast, audio description information may be referred to broadcast
+     * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio Language
+     * Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN 300 468).
+     *
+     * @throws IllegalStateException if not called on an audio track
+     */
+    public boolean isAudioDescription() {
+        if (mType != TYPE_AUDIO) {
+            throw new IllegalStateException("Not an audio track");
+        }
+        return mAudioDescription;
+    }
+
+    /**
      * Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
      * tracks.
      *
@@ -266,6 +286,7 @@
         dest.writeInt(mEncrypted ? 1 : 0);
         dest.writeInt(mAudioChannelCount);
         dest.writeInt(mAudioSampleRate);
+        dest.writeInt(mAudioDescription ? 1 : 0);
         dest.writeInt(mVideoWidth);
         dest.writeInt(mVideoHeight);
         dest.writeFloat(mVideoFrameRate);
@@ -296,7 +317,8 @@
         switch (mType) {
             case TYPE_AUDIO:
                 return mAudioChannelCount == obj.mAudioChannelCount
-                        && mAudioSampleRate == obj.mAudioSampleRate;
+                        && mAudioSampleRate == obj.mAudioSampleRate
+                        && mAudioDescription == obj.mAudioDescription;
 
             case TYPE_VIDEO:
                 return mVideoWidth == obj.mVideoWidth
@@ -338,6 +360,7 @@
         private boolean mEncrypted;
         private int mAudioChannelCount;
         private int mAudioSampleRate;
+        private boolean mAudioDescription;
         private int mVideoWidth;
         private int mVideoHeight;
         private float mVideoFrameRate;
@@ -430,6 +453,27 @@
         }
 
         /**
+         * Sets the audio description attribute of the audio. Valid only for {@link #TYPE_AUDIO}
+         * tracks.
+         *
+         * <p>For example of broadcast, audio description information may be referred to broadcast
+         * standard (e.g. ISO 639 Language Descriptor of ISO/IEC 13818-1, Supplementary Audio
+         * Language Descriptor, AC-3 Descriptor, Enhanced AC-3 Descriptor, AAC Descriptor of ETSI EN
+         * 300 468).
+         *
+         * @param audioDescription The audio description attribute of the audio.
+         * @throws IllegalStateException if not called on an audio track
+         */
+        @NonNull
+        public Builder setAudioDescription(boolean audioDescription) {
+            if (mType != TYPE_AUDIO) {
+                throw new IllegalStateException("Not an audio track");
+            }
+            mAudioDescription = audioDescription;
+            return this;
+        }
+
+        /**
          * Sets the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
          * tracks.
          *
@@ -531,8 +575,9 @@
          */
         public TvTrackInfo build() {
             return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted,
-                    mAudioChannelCount, mAudioSampleRate, mVideoWidth, mVideoHeight,
-                    mVideoFrameRate, mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
+                    mAudioChannelCount, mAudioSampleRate, mAudioDescription, mVideoWidth,
+                    mVideoHeight, mVideoFrameRate, mVideoPixelAspectRatio,
+                    mVideoActiveFormatDescription, mExtra);
         }
     }
 }
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
new file mode 100644
index 0000000..bda166e
--- /dev/null
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Capabilities info for Demux.
+ * @hide
+ */
+public class DemuxCapabilities {
+    private final int mNumDemux;
+    private final int mNumRecord;
+    private final int mNumPlayback;
+    private final int mNumTsFilter;
+    private final int mNumSectionFilter;
+    private final int mNumAudioFilter;
+    private final int mNumVideoFilter;
+    private final int mNumPesFilter;
+    private final int mNumPcrFilter;
+    private final int mNumBytesInSectionFilter;
+    private final int mFilterCaps;
+    private final int[] mLinkCaps;
+
+    DemuxCapabilities(int numDemux, int numRecord, int numPlayback, int numTsFilter,
+            int numSectionFilter, int numAudioFilter, int numVideoFilter, int numPesFilter,
+            int numPcrFilter, int numBytesInSectionFilter, int filterCaps, int[] linkCaps) {
+        mNumDemux = numDemux;
+        mNumRecord = numRecord;
+        mNumPlayback = numPlayback;
+        mNumTsFilter = numTsFilter;
+        mNumSectionFilter = numSectionFilter;
+        mNumAudioFilter = numAudioFilter;
+        mNumVideoFilter = numVideoFilter;
+        mNumPesFilter = numPesFilter;
+        mNumPcrFilter = numPcrFilter;
+        mNumBytesInSectionFilter = numBytesInSectionFilter;
+        mFilterCaps = filterCaps;
+        mLinkCaps = linkCaps;
+    }
+
+    /** Gets total number of demuxes. */
+    public int getNumDemux() {
+        return mNumDemux;
+    }
+    /** Gets max number of recordings at a time. */
+    public int getNumRecord() {
+        return mNumRecord;
+    }
+    /** Gets max number of playbacks at a time. */
+    public int getNumPlayback() {
+        return mNumPlayback;
+    }
+    /** Gets number of TS filters. */
+    public int getNumTsFilter() {
+        return mNumTsFilter;
+    }
+    /** Gets number of section filters. */
+    public int getNumSectionFilter() {
+        return mNumSectionFilter;
+    }
+    /** Gets number of audio filters. */
+    public int getNumAudioFilter() {
+        return mNumAudioFilter;
+    }
+    /** Gets number of video filters. */
+    public int getNumVideoFilter() {
+        return mNumVideoFilter;
+    }
+    /** Gets number of PES filters. */
+    public int getNumPesFilter() {
+        return mNumPesFilter;
+    }
+    /** Gets number of PCR filters. */
+    public int getNumPcrFilter() {
+        return mNumPcrFilter;
+    }
+    /** Gets number of bytes in the mask of a section filter. */
+    public int getNumBytesInSectionFilter() {
+        return mNumBytesInSectionFilter;
+    }
+    /**
+     * Gets filter capabilities in bit field.
+     *
+     * The bits of the returned value is corresponding to the types in
+     * {@link TunerConstants.FilterType}.
+     */
+    public int getFilterCapabilities() {
+        return mFilterCaps;
+    }
+
+    /**
+     * Gets link capabilities.
+     *
+     * The returned array contains the same elements as the number of types in
+     * {@link TunerConstants.FilterType}.
+     * The ith element represents the filter's capability as the source for the ith type
+     */
+    public int[] getLinkCapabilities() {
+        return mLinkCaps;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/FilterSettings.java b/media/java/android/media/tv/tuner/FilterConfiguration.java
similarity index 83%
rename from media/java/android/media/tv/tuner/FilterSettings.java
rename to media/java/android/media/tv/tuner/FilterConfiguration.java
index 673b3d9..b80a82a 100644
--- a/media/java/android/media/tv/tuner/FilterSettings.java
+++ b/media/java/android/media/tv/tuner/FilterConfiguration.java
@@ -22,33 +22,37 @@
 import java.util.List;
 
 /**
- * Demux Filter settings.
+ * Demux Filter configuration.
  *
  * @hide
  */
-public abstract class FilterSettings {
+public abstract class FilterConfiguration {
     @Nullable
     protected final Settings mSettings;
 
-    protected FilterSettings(Settings settings) {
+    protected FilterConfiguration(Settings settings) {
         mSettings = settings;
     }
 
     /**
-     * Gets filter settings type
+     * Gets filter configuration type
      */
     @FilterType
     public abstract int getType();
 
+    public Settings getSettings() {
+        return mSettings;
+    }
+
     // TODO: more builders and getters
 
     /**
-     *  Filter Settings for a TS filter.
+     *  Filter configuration for a TS filter.
      */
-    public static class TsFilterSettings extends FilterSettings {
+    public static class TsFilterConfiguration extends FilterConfiguration {
         private int mTpid;
 
-        private TsFilterSettings(Settings settings, int tpid) {
+        private TsFilterConfiguration(Settings settings, int tpid) {
             super(settings);
             mTpid = tpid;
         }
@@ -66,7 +70,7 @@
         }
 
         /**
-         * Builder for TsFilterSettings.
+         * Builder for TsFilterConfiguration.
          */
         public static class Builder {
             private Settings mSettings;
@@ -89,21 +93,21 @@
             }
 
             /**
-             * Builds a TsFilterSettings instance.
+             * Builds a TsFilterConfiguration instance.
              */
-            public TsFilterSettings build() {
-                return new TsFilterSettings(mSettings, mTpid);
+            public TsFilterConfiguration build() {
+                return new TsFilterConfiguration(mSettings, mTpid);
             }
         }
     }
 
     /**
-     *  Filter Settings for a MMTP filter.
+     *  Filter configuration for a MMTP filter.
      */
-    public static class MmtpFilterSettings extends FilterSettings {
+    public static class MmtpFilterConfiguration extends FilterConfiguration {
         private int mMmtpPid;
 
-        public MmtpFilterSettings(Settings settings) {
+        public MmtpFilterConfiguration(Settings settings) {
             super(settings);
         }
 
@@ -115,16 +119,16 @@
 
 
     /**
-     *  Filter Settings for a IP filter.
+     *  Filter configuration for a IP filter.
      */
-    public static class IpFilterSettings extends FilterSettings {
+    public static class IpFilterConfiguration extends FilterConfiguration {
         private byte[] mSrcIpAddress;
         private byte[] mDstIpAddress;
         private int mSrcPort;
         private int mDstPort;
         private boolean mPassthrough;
 
-        public IpFilterSettings(Settings settings) {
+        public IpFilterConfiguration(Settings settings) {
             super(settings);
         }
 
@@ -136,14 +140,14 @@
 
 
     /**
-     *  Filter Settings for a TLV filter.
+     *  Filter configuration for a TLV filter.
      */
-    public static class TlvFilterSettings extends FilterSettings {
+    public static class TlvFilterConfiguration extends FilterConfiguration {
         private int mPacketType;
         private boolean mIsCompressedIpPacket;
         private boolean mPassthrough;
 
-        public TlvFilterSettings(Settings settings) {
+        public TlvFilterConfiguration(Settings settings) {
             super(settings);
         }
 
@@ -155,13 +159,13 @@
 
 
     /**
-     *  Filter Settings for a ALP filter.
+     *  Filter configuration for a ALP filter.
      */
-    public static class AlpFilterSettings extends FilterSettings {
+    public static class AlpFilterConfiguration extends FilterConfiguration {
         private int mPacketType;
         private int mLengthType;
 
-        public AlpFilterSettings(Settings settings) {
+        public AlpFilterConfiguration(Settings settings) {
             super(settings);
         }
 
diff --git a/media/java/android/media/tv/tuner/FilterEvent.java b/media/java/android/media/tv/tuner/FilterEvent.java
deleted file mode 100644
index 7c165ce..0000000
--- a/media/java/android/media/tv/tuner/FilterEvent.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.media.tv.tuner;
-
-import android.os.NativeHandle;
-
-/**
- * Demux filter event.
- *
- * @hide
- */
-public abstract class FilterEvent {
-
-    /**
-     * Section event.
-     */
-    public static class SectionEvent extends FilterEvent {
-        private int mTableId;
-        private int mVersion;
-        private int mSectionNum;
-        private int mDataLength;
-    }
-
-    /**
-     * Media event.
-     */
-    public static class MediaEvent extends FilterEvent {
-        private int mStreamId;
-        private boolean mIsPtsPresent;
-        private long mPts;
-        private int mDataLength;
-        private NativeHandle mHandle;
-        private boolean mIsSecureMemory;
-        private int mMpuSequenceNumber;
-        private boolean mIsPrivateData;
-        private AudioExtraMetaData mExtraMetaData;
-    }
-
-    /**
-     * PES event.
-     */
-    public static class PesEvent extends FilterEvent {
-        private int mStreamId;
-        private int mDataLength;
-        private int mMpuSequenceNumber;
-    }
-
-    /**
-     * TS record event.
-     */
-    public static class TsRecordEvent extends FilterEvent {
-        private int mTpid;
-        private int mIndexMask;
-        private long mByteNumber;
-    }
-
-    /**
-     * MMPT record event.
-     */
-    public static class MmtpRecordEvent extends FilterEvent {
-        private int mScHevcIndexMask;
-        private long mByteNumber;
-    }
-
-    /**
-     * Download event.
-     */
-    public static class DownloadEvent extends FilterEvent {
-        private int mItemId;
-        private int mMpuSequenceNumber;
-        private int mItemFragmentIndex;
-        private int mLastItemFragmentIndex;
-        private int mDataLength;
-    }
-
-    /**
-     * IP payload event.
-     */
-    public static class IpPayloadEvent extends FilterEvent {
-        private int mDataLength;
-    }
-
-    /**
-     * TEMI event.
-     */
-    public static class TemiEvent extends FilterEvent {
-        private long mPts;
-        private byte mDescrTag;
-        private byte[] mDescrData;
-    }
-
-    /**
-     *  Extra Meta Data from AD (Audio Descriptor) according to
-     *  ETSI TS 101 154 V2.1.1.
-     */
-    public static class AudioExtraMetaData {
-        private byte mAdFade;
-        private byte mAdPan;
-        private byte mVersionTextTag;
-        private byte mAdGainCenter;
-        private byte mAdGainFront;
-        private byte mAdGainSurround;
-    }
-}
diff --git a/media/java/android/media/tv/tuner/FrontendCapabilities.java b/media/java/android/media/tv/tuner/FrontendCapabilities.java
new file mode 100644
index 0000000..fcfd7c8
--- /dev/null
+++ b/media/java/android/media/tv/tuner/FrontendCapabilities.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+/**
+ * Frontend Capabilities.
+ * @hide
+ */
+public class FrontendCapabilities {
+    /** Analog Capabilities. */
+    public class Analog extends FrontendCapabilities {
+        private final int mTypeCap;
+        private final int mSifStandardCap;
+
+        Analog(int typeCap, int sifStandardCap) {
+            mTypeCap = typeCap;
+            mSifStandardCap = sifStandardCap;
+        }
+        /**
+         * Gets type capability.
+         */
+        public int getTypeCapability() {
+            return mTypeCap;
+        }
+        /** Gets SIF standard capability. */
+        public int getSifStandardCapability() {
+            return mSifStandardCap;
+        }
+    }
+
+    /** ATSC Capabilities. */
+    public class Atsc extends FrontendCapabilities {
+        private final int mModulationCap;
+
+        Atsc(int modulationCap) {
+            mModulationCap = modulationCap;
+        }
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+    }
+
+    /** ATSC-3 Capabilities. */
+    public class Atsc3 extends FrontendCapabilities {
+        private final int mBandwidthCap;
+        private final int mModulationCap;
+        private final int mTimeInterleaveModeCap;
+        private final int mCodeRateCap;
+        private final int mFecCap;
+        private final int mDemodOutputFormatCap;
+
+        Atsc3(int bandwidthCap, int modulationCap, int timeInterleaveModeCap, int codeRateCap,
+                int fecCap, int demodOutputFormatCap) {
+            mBandwidthCap = bandwidthCap;
+            mModulationCap = modulationCap;
+            mTimeInterleaveModeCap = timeInterleaveModeCap;
+            mCodeRateCap = codeRateCap;
+            mFecCap = fecCap;
+            mDemodOutputFormatCap = demodOutputFormatCap;
+        }
+
+        /** Gets bandwidth capability. */
+        public int getBandwidthCapability() {
+            return mBandwidthCap;
+        }
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+        /** Gets time interleave mod capability. */
+        public int getTimeInterleaveModeCapability() {
+            return mTimeInterleaveModeCap;
+        }
+        /** Gets code rate capability. */
+        public int getCodeRateCapability() {
+            return mCodeRateCap;
+        }
+        /** Gets FEC capability. */
+        public int getFecCapability() {
+            return mFecCap;
+        }
+        /** Gets demodulator output format capability. */
+        public int getDemodOutputFormatCapability() {
+            return mDemodOutputFormatCap;
+        }
+    }
+
+    /** DVBS Capabilities. */
+    public class Dvbs extends FrontendCapabilities {
+        private final int mModulationCap;
+        private final long mInnerFecCap;
+        private final int mStandard;
+
+        Dvbs(int modulationCap, long innerFecCap, int standard) {
+            mModulationCap = modulationCap;
+            mInnerFecCap = innerFecCap;
+            mStandard = standard;
+        }
+
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+        /** Gets inner FEC capability. */
+        public long getInnerFecCapability() {
+            return mInnerFecCap;
+        }
+        /** Gets DVBS standard capability. */
+        public int getStandardCapability() {
+            return mStandard;
+        }
+    }
+
+    /** DVBC Capabilities. */
+    public class Dvbc extends FrontendCapabilities {
+        private final int mModulationCap;
+        private final int mFecCap;
+        private final int mAnnexCap;
+
+        Dvbc(int modulationCap, int fecCap, int annexCap) {
+            mModulationCap = modulationCap;
+            mFecCap = fecCap;
+            mAnnexCap = annexCap;
+        }
+
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+        /** Gets FEC capability. */
+        public int getFecCapability() {
+            return mFecCap;
+        }
+        /** Gets annex capability. */
+        public int getAnnexCapability() {
+            return mAnnexCap;
+        }
+    }
+
+    /** DVBT Capabilities. */
+    public class Dvbt extends FrontendCapabilities {
+        private final int mTransmissionModeCap;
+        private final int mBandwidthCap;
+        private final int mConstellationCap;
+        private final int mCoderateCap;
+        private final int mHierarchyCap;
+        private final int mGuardIntervalCap;
+        private final boolean mIsT2Supported;
+        private final boolean mIsMisoSupported;
+
+        Dvbt(int transmissionModeCap, int bandwidthCap, int constellationCap, int coderateCap,
+                int hierarchyCap, int guardIntervalCap, boolean isT2Supported,
+                boolean isMisoSupported) {
+            mTransmissionModeCap = transmissionModeCap;
+            mBandwidthCap = bandwidthCap;
+            mConstellationCap = constellationCap;
+            mCoderateCap = coderateCap;
+            mHierarchyCap = hierarchyCap;
+            mGuardIntervalCap = guardIntervalCap;
+            mIsT2Supported = isT2Supported;
+            mIsMisoSupported = isMisoSupported;
+        }
+
+        /** Gets transmission mode capability. */
+        public int getTransmissionModeCapability() {
+            return mTransmissionModeCap;
+        }
+        /** Gets bandwidth capability. */
+        public int getBandwidthCapability() {
+            return mBandwidthCap;
+        }
+        /** Gets constellation capability. */
+        public int getConstellationCapability() {
+            return mConstellationCap;
+        }
+        /** Gets code rate capability. */
+        public int getCodeRateCapability() {
+            return mCoderateCap;
+        }
+        /** Gets hierarchy capability. */
+        public int getHierarchyCapability() {
+            return mHierarchyCap;
+        }
+        /** Gets guard interval capability. */
+        public int getGuardIntervalCapability() {
+            return mGuardIntervalCap;
+        }
+        /** Returns whether T2 is supported. */
+        public boolean getIsT2Supported() {
+            return mIsT2Supported;
+        }
+        /** Returns whether MISO is supported. */
+        public boolean getIsMisoSupported() {
+            return mIsMisoSupported;
+        }
+    }
+
+    /** ISDBS Capabilities. */
+    public class Isdbs extends FrontendCapabilities {
+        private final int mModulationCap;
+        private final int mCoderateCap;
+
+        Isdbs(int modulationCap, int coderateCap) {
+            mModulationCap = modulationCap;
+            mCoderateCap = coderateCap;
+        }
+
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+        /** Gets code rate capability. */
+        public int getCodeRateCapability() {
+            return mCoderateCap;
+        }
+    }
+
+    /** ISDBS-3 Capabilities. */
+    public class Isdbs3 extends FrontendCapabilities {
+        private final int mModulationCap;
+        private final int mCoderateCap;
+
+        Isdbs3(int modulationCap, int coderateCap) {
+            mModulationCap = modulationCap;
+            mCoderateCap = coderateCap;
+        }
+
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+        /** Gets code rate capability. */
+        public int getCodeRateCapability() {
+            return mCoderateCap;
+        }
+    }
+
+    /** ISDBC Capabilities. */
+    public class Isdbc extends FrontendCapabilities {
+        private final int mModeCap;
+        private final int mBandwidthCap;
+        private final int mModulationCap;
+        private final int mCoderateCap;
+        private final int mGuardIntervalCap;
+
+        Isdbc(int modeCap, int bandwidthCap, int modulationCap, int coderateCap,
+                int guardIntervalCap) {
+            mModeCap = modeCap;
+            mBandwidthCap = bandwidthCap;
+            mModulationCap = modulationCap;
+            mCoderateCap = coderateCap;
+            mGuardIntervalCap = guardIntervalCap;
+        }
+
+        /** Gets mode capability. */
+        public int getModeCapability() {
+            return mModeCap;
+        }
+        /** Gets bandwidth capability. */
+        public int getBandwidthCapability() {
+            return mBandwidthCap;
+        }
+        /** Gets modulation capability. */
+        public int getModulationCapability() {
+            return mModulationCap;
+        }
+        /** Gets code rate capability. */
+        public int getCodeRateCapability() {
+            return mCoderateCap;
+        }
+        /** Gets guard interval capability. */
+        public int getGuardIntervalCapability() {
+            return mGuardIntervalCap;
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/FrontendSettings.java b/media/java/android/media/tv/tuner/FrontendSettings.java
index 531e210..e2e9910 100644
--- a/media/java/android/media/tv/tuner/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/FrontendSettings.java
@@ -19,8 +19,6 @@
 import android.annotation.SystemApi;
 import android.media.tv.tuner.TunerConstants.FrontendSettingsType;
 
-import java.util.List;
-
 /**
  * Frontend settings for tune and scan operations.
  * @hide
@@ -29,7 +27,8 @@
 public abstract class FrontendSettings {
     private final int mFrequency;
 
-    FrontendSettings(int frequency) {
+    /** @hide */
+    public FrontendSettings(int frequency) {
         mFrequency = frequency;
     }
 
@@ -48,282 +47,4 @@
         return mFrequency;
     }
 
-    // TODO: use hal constants for enum fields
-    // TODO: javaDoc
-    // TODO: add builders and getters for other settings type
-
-    /**
-     * Frontend settings for analog.
-     * @hide
-     */
-    public static class FrontendAnalogSettings extends FrontendSettings {
-        private int mAnalogType;
-        private int mSifStandard;
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_ANALOG;
-        }
-
-        public int getAnalogType() {
-            return mAnalogType;
-        }
-
-        public int getSifStandard() {
-            return mSifStandard;
-        }
-
-        /**
-         * Creates a new builder object.
-         */
-        public static Builder newBuilder() {
-            return new Builder();
-        }
-
-        private FrontendAnalogSettings(int frequency, int analogType, int sifStandard) {
-            super(frequency);
-            mAnalogType = analogType;
-            mSifStandard = sifStandard;
-        }
-
-        /**
-         * Builder for FrontendAnalogSettings.
-         */
-        public static class Builder {
-            private int mFrequency;
-            private int mAnalogType;
-            private int mSifStandard;
-
-            private Builder() {}
-
-            /**
-             * Sets frequency.
-             */
-            public Builder setFrequency(int frequency) {
-                mFrequency = frequency;
-                return this;
-            }
-
-            /**
-             * Sets analog type.
-             */
-            public Builder setAnalogType(int analogType) {
-                mAnalogType = analogType;
-                return this;
-            }
-
-            /**
-             * Sets sif standard.
-             */
-            public Builder setSifStandard(int sifStandard) {
-                mSifStandard = sifStandard;
-                return this;
-            }
-
-            /**
-             * Builds a FrontendAnalogSettings instance.
-             */
-            public FrontendAnalogSettings build() {
-                return new FrontendAnalogSettings(mFrequency, mAnalogType, mSifStandard);
-            }
-        }
-    }
-
-    /**
-     * Frontend settings for ATSC.
-     * @hide
-     */
-    public static class FrontendAtscSettings extends FrontendSettings {
-        public int modulation;
-
-        FrontendAtscSettings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_ATSC;
-        }
-    }
-
-    /**
-     * Frontend settings for ATSC-3.
-     * @hide
-     */
-    public static class FrontendAtsc3Settings extends FrontendSettings {
-        public int bandwidth;
-        public byte demodOutputFormat;
-        public List<FrontendAtsc3PlpSettings> plpSettings;
-
-        FrontendAtsc3Settings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_ATSC3;
-        }
-    }
-
-    /**
-     * Frontend settings for DVBS.
-     * @hide
-     */
-    public static class FrontendDvbsSettings extends FrontendSettings {
-        public int modulation;
-        public FrontendDvbsCodeRate coderate;
-        public int symbolRate;
-        public int rolloff;
-        public int pilot;
-        public int inputStreamId;
-        public byte standard;
-
-        FrontendDvbsSettings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_DVBS;
-        }
-    }
-
-    /**
-     * Frontend settings for DVBC.
-     * @hide
-     */
-    public static class FrontendDvbcSettings extends FrontendSettings {
-        public int modulation;
-        public long fec;
-        public int symbolRate;
-        public int outerFec;
-        public byte annex;
-        public int spectralInversion;
-
-        FrontendDvbcSettings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_DVBC;
-        }
-    }
-
-    /**
-     * Frontend settings for DVBT.
-     * @hide
-     */
-    public static class FrontendDvbtSettings extends FrontendSettings {
-        public int transmissionMode;
-        public int bandwidth;
-        public int constellation;
-        public int hierarchy;
-        public int hpCoderate;
-        public int lpCoderate;
-        public int guardInterval;
-        public boolean isHighPriority;
-        public byte standard;
-        public boolean isMiso;
-        public int plpMode;
-        public byte plpId;
-        public byte plpGroupId;
-
-        FrontendDvbtSettings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_DVBT;
-        }
-    }
-
-    /**
-     * Frontend settings for ISDBS.
-     * @hide
-     */
-    public static class FrontendIsdbsSettings extends FrontendSettings {
-        public int streamId;
-        public int streamIdType;
-        public int modulation;
-        public int coderate;
-        public int symbolRate;
-        public int rolloff;
-
-        FrontendIsdbsSettings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_ISDBS;
-        }
-    }
-
-    /**
-     * Frontend settings for ISDBS-3.
-     * @hide
-     */
-    public static class FrontendIsdbs3Settings extends FrontendSettings {
-        public int streamId;
-        public int streamIdType;
-        public int modulation;
-        public int coderate;
-        public int symbolRate;
-        public int rolloff;
-
-        FrontendIsdbs3Settings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_ISDBS3;
-        }
-    }
-
-    /**
-     * Frontend settings for ISDBT.
-     * @hide
-     */
-    public static class FrontendIsdbtSettings extends FrontendSettings {
-        public int modulation;
-        public int bandwidth;
-        public int coderate;
-        public int guardInterval;
-        public int serviceAreaId;
-
-        FrontendIsdbtSettings(int frequency) {
-            super(frequency);
-        }
-
-        @Override
-        public int getType() {
-            return TunerConstants.FRONTEND_TYPE_ISDBT;
-        }
-    }
-
-    /**
-     * PLP settings for ATSC-3.
-     * @hide
-     */
-    public static class FrontendAtsc3PlpSettings {
-        public byte plpId;
-        public int modulation;
-        public int interleaveMode;
-        public int codeRate;
-        public int fec;
-    }
-
-    /**
-     * Code rate for DVBS.
-     * @hide
-     */
-    public static class FrontendDvbsCodeRate {
-        public long fec;
-        public boolean isLinear;
-        public boolean isShortFrames;
-        public int bitsPer1000Symbol;
-    }
 }
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index b8ab7ee..43f9a89 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,7 +21,9 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.Context;
+import android.media.tv.tuner.FilterConfiguration.Settings;
 import android.media.tv.tuner.TunerConstants.DemuxPidType;
+import android.media.tv.tuner.TunerConstants.FilterStatus;
 import android.media.tv.tuner.TunerConstants.FilterSubtype;
 import android.media.tv.tuner.TunerConstants.FilterType;
 import android.media.tv.tuner.TunerConstants.FrontendScanType;
@@ -29,10 +31,15 @@
 import android.media.tv.tuner.TunerConstants.LnbTone;
 import android.media.tv.tuner.TunerConstants.LnbVoltage;
 import android.media.tv.tuner.TunerConstants.Result;
+import android.media.tv.tuner.filter.FilterEvent;
+import android.media.tv.tuner.frontend.FrontendCallback;
+import android.media.tv.tuner.frontend.FrontendInfo;
+import android.media.tv.tuner.frontend.FrontendStatus;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 
+import java.io.FileDescriptor;
 import java.util.List;
 
 /**
@@ -110,6 +117,11 @@
     private native int nativeSetLnb(int lnbId);
     private native int nativeSetLna(boolean enable);
     private native FrontendStatus[] nativeGetFrontendStatus(int[] statusTypes);
+    private native int nativeGetAvSyncHwId(Filter filter);
+    private native long nativeGetAvSyncTime(int avSyncId);
+    private native int nativeConnectCiCam(int ciCamId);
+    private native int nativeDisconnectCiCam();
+    private native FrontendInfo nativeGetFrontendInfo(int id);
     private native Filter nativeOpenFilter(int type, int subType, int bufferSize);
 
     private native List<Integer> nativeGetLnbIds();
@@ -119,24 +131,7 @@
 
     private native Dvr nativeOpenDvr(int type, int bufferSize);
 
-    /**
-     * Frontend Callback.
-     *
-     * @hide
-     */
-    public interface FrontendCallback {
-
-        /**
-         * Invoked when there is a frontend event.
-         */
-        void onEvent(int frontendEventType);
-
-        /**
-         * Invoked when there is a scan message.
-         * @param msg
-         */
-        void onScanMessage(ScanMessage msg);
-    }
+    private static native DemuxCapabilities nativeGetDemuxCapabilities();
 
     /**
      * LNB Callback.
@@ -160,19 +155,23 @@
     }
 
     /**
-     * Frontend Callback.
-     *
-     * @hide
+     * Callback interface for receiving information from the corresponding filters.
      */
     public interface FilterCallback {
         /**
          * Invoked when there are filter events.
+         *
+         * @param filter the corresponding filter which sent the events.
+         * @param events the filter events sent from the filter.
          */
-        void onFilterEvent(FilterEvent[] events);
+        void onFilterEvent(@NonNull Filter filter, @NonNull FilterEvent[] events);
         /**
          * Invoked when filter status changed.
+         *
+         * @param filter the corresponding filter whose status is changed.
+         * @param status the new status of the filter.
          */
-        void onFilterStatus(int status);
+        void onFilterStatusChanged(@NonNull Filter filter, @FilterStatus int status);
     }
 
     /**
@@ -218,7 +217,7 @@
                 case MSG_ON_FILTER_STATUS: {
                     Filter filter = (Filter) msg.obj;
                     if (filter.mCallback != null) {
-                        filter.mCallback.onFilterStatus(msg.arg1);
+                        filter.mCallback.onFilterStatusChanged(filter, msg.arg1);
                     }
                     break;
                 }
@@ -351,6 +350,86 @@
         return nativeGetFrontendStatus(statusTypes);
     }
 
+    /**
+     * Gets hardware sync ID for audio and video.
+     *
+     * @param filter the filter instance for the hardware sync ID.
+     * @return the id of hardware A/V sync.
+     * @hide
+     */
+    public int getAvSyncHwId(Filter filter) {
+        return nativeGetAvSyncHwId(filter);
+    }
+    /**
+     * Gets the current timestamp for A/V sync
+     *
+     * The timestamp is maintained by hardware. The timestamp based on 90KHz, and it's format is the
+     * same as PTS (Presentation Time Stamp).
+     *
+     * @param avSyncHwId the hardware id of A/V sync.
+     * @return the current timestamp of hardware A/V sync.
+     * @hide
+     */
+    public long getAvSyncTime(int avSyncHwId) {
+        return nativeGetAvSyncTime(avSyncHwId);
+    }
+
+
+    /**
+     * Connects Conditional Access Modules (CAM) through Common Interface (CI)
+     *
+     * The demux uses the output from the frontend as the input by default, and must change to use
+     * the output from CI-CAM as the input after this call.
+     *
+     * @param ciCamId specify CI-CAM Id to connect.
+     * @return result status of the operation.
+     * @hide
+     */
+    @Result
+    public int connectCiCam(int ciCamId) {
+        return nativeConnectCiCam(ciCamId);
+    }
+
+    /**
+     * Disconnects Conditional Access Modules (CAM)
+     *
+     * The demux will use the output from the frontend as the input after this call.
+     *
+     * @return result status of the operation.
+     * @hide
+     */
+    @Result
+    public int disconnectCiCam() {
+        return nativeDisconnectCiCam();
+    }
+
+    /**
+     * Retrieve the frontend information.
+     * @hide
+     */
+    public FrontendInfo getFrontendInfo() {
+        if (mFrontend == null) {
+            throw new IllegalStateException("frontend is not initialized");
+        }
+        return nativeGetFrontendInfo(mFrontend.mId);
+    }
+
+    /**
+     * Gets frontend ID.
+     * @hide
+     */
+    public int getFrontendId() {
+        if (mFrontend == null) {
+            throw new IllegalStateException("frontend is not initialized");
+        }
+        return mFrontend.mId;
+    }
+
+    /** @hide */
+    private static DemuxCapabilities getDemuxCapabilities() {
+        return nativeGetDemuxCapabilities();
+    }
+
     private List<Integer> getFrontendIds() {
         mFrontendIds = nativeGetFrontendIds();
         return mFrontendIds;
@@ -373,13 +452,18 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Tuner data filter.
+     *
+     * <p> This class is used to filter wanted data according to the filter's configuration.
+     */
     public class Filter {
         private long mNativeContext;
         private FilterCallback mCallback;
         int mId;
 
-        private native int nativeConfigureFilter(int type, int subType, FilterSettings settings);
+        private native int nativeConfigureFilter(
+                int type, int subType, FilterConfiguration settings);
         private native int nativeGetId();
         private native int nativeSetDataSource(Filter source);
         private native int nativeStartFilter();
@@ -404,11 +488,13 @@
          *
          * @param settings the settings of the filter.
          * @return result status of the operation.
+         * @hide
          */
-        public int configure(FilterSettings settings) {
+        public int configure(FilterConfiguration settings) {
             int subType = -1;
-            if (settings.mSettings != null) {
-                subType = settings.mSettings.getType();
+            Settings s = settings.getSettings();
+            if (s != null) {
+                subType = s.getType();
             }
             return nativeConfigureFilter(settings.getType(), subType, settings);
         }
@@ -417,6 +503,7 @@
          * Gets the filter Id.
          *
          * @return the hardware resource Id for the filter.
+         * @hide
          */
         public int getId() {
             return nativeGetId();
@@ -433,6 +520,7 @@
          * @param source the filter instance which provides data input. Switch to
          * use demux as data source if the filter instance is NULL.
          * @return result status of the operation.
+         * @hide
          */
         public int setDataSource(@Nullable Filter source) {
             return nativeSetDataSource(source);
@@ -442,6 +530,7 @@
          * Starts the filter.
          *
          * @return result status of the operation.
+         * @hide
          */
         public int start() {
             return nativeStartFilter();
@@ -452,6 +541,7 @@
          * Stops the filter.
          *
          * @return result status of the operation.
+         * @hide
          */
         public int stop() {
             return nativeStopFilter();
@@ -461,11 +551,13 @@
          * Flushes the filter.
          *
          * @return result status of the operation.
+         * @hide
          */
         public int flush() {
             return nativeFlushFilter();
         }
 
+        /** @hide */
         public int read(@NonNull byte[] buffer, int offset, int size) {
             size = Math.min(size, buffer.length - offset);
             return nativeRead(buffer, offset, size);
@@ -475,6 +567,7 @@
          * Release the Filter instance.
          *
          * @return result status of the operation.
+         * @hide
          */
         public int close() {
             return nativeClose();
@@ -708,6 +801,11 @@
         private native int nativeStopDvr();
         private native int nativeFlushDvr();
         private native int nativeClose();
+        private native void nativeSetFileDescriptor(FileDescriptor fd);
+        private native int nativeRead(int size);
+        private native int nativeRead(byte[] bytes, int offset, int size);
+        private native int nativeWrite(int size);
+        private native int nativeWrite(byte[] bytes, int offset, int size);
 
         private Dvr() {}
 
@@ -780,6 +878,45 @@
         public int close() {
             return nativeClose();
         }
+
+        /**
+         * Sets file descriptor to read/write data.
+         */
+        public void setFileDescriptor(FileDescriptor fd) {
+            nativeSetFileDescriptor(fd);
+        }
+
+        /**
+         * Reads data from the file for DVR playback.
+         */
+        public int read(int size) {
+            return nativeRead(size);
+        }
+
+        /**
+         * Reads data from the buffer for DVR playback.
+         */
+        public int read(@NonNull byte[] bytes, int offset, int size) {
+            if (size + offset > bytes.length) {
+                throw new ArrayIndexOutOfBoundsException(
+                        "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+            }
+            return nativeRead(bytes, offset, size);
+        }
+
+        /**
+         * Writes recording data to file.
+         */
+        public int write(int size) {
+            return nativeWrite(size);
+        }
+
+        /**
+         * Writes recording data to buffer.
+         */
+        public int write(@NonNull byte[] bytes, int offset, int size) {
+            return nativeWrite(bytes, offset, size);
+        }
     }
 
     private Dvr openDvr(int type, int bufferSize) {
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index e611431..d24e582 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -18,151 +18,252 @@
 
 import android.annotation.IntDef;
 import android.annotation.LongDef;
+import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
+ * Constants for tuner framework.
+ *
  * @hide
  */
-final class TunerConstants {
+@SystemApi
+public final class TunerConstants {
+    /** @hide */
     public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
+    /** @hide */
     public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({FRONTEND_TYPE_UNDEFINED, FRONTEND_TYPE_ANALOG, FRONTEND_TYPE_ATSC, FRONTEND_TYPE_ATSC3,
             FRONTEND_TYPE_DVBC, FRONTEND_TYPE_DVBS, FRONTEND_TYPE_DVBT, FRONTEND_TYPE_ISDBS,
             FRONTEND_TYPE_ISDBS3, FRONTEND_TYPE_ISDBT})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendType {}
 
+    /** @hide */
     public static final int FRONTEND_TYPE_UNDEFINED = Constants.FrontendType.UNDEFINED;
+    /** @hide */
     public static final int FRONTEND_TYPE_ANALOG = Constants.FrontendType.ANALOG;
+    /** @hide */
     public static final int FRONTEND_TYPE_ATSC = Constants.FrontendType.ATSC;
+    /** @hide */
     public static final int FRONTEND_TYPE_ATSC3 = Constants.FrontendType.ATSC3;
+    /** @hide */
     public static final int FRONTEND_TYPE_DVBC = Constants.FrontendType.DVBC;
+    /** @hide */
     public static final int FRONTEND_TYPE_DVBS = Constants.FrontendType.DVBS;
+    /** @hide */
     public static final int FRONTEND_TYPE_DVBT = Constants.FrontendType.DVBT;
+    /** @hide */
     public static final int FRONTEND_TYPE_ISDBS = Constants.FrontendType.ISDBS;
+    /** @hide */
     public static final int FRONTEND_TYPE_ISDBS3 = Constants.FrontendType.ISDBS3;
+    /** @hide */
     public static final int FRONTEND_TYPE_ISDBT = Constants.FrontendType.ISDBT;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({FRONTEND_EVENT_TYPE_LOCKED, FRONTEND_EVENT_TYPE_NO_SIGNAL,
             FRONTEND_EVENT_TYPE_LOST_LOCK})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendEventType {}
-
+    /** @hide */
     public static final int FRONTEND_EVENT_TYPE_LOCKED = Constants.FrontendEventType.LOCKED;
+    /** @hide */
     public static final int FRONTEND_EVENT_TYPE_NO_SIGNAL = Constants.FrontendEventType.NO_SIGNAL;
+    /** @hide */
     public static final int FRONTEND_EVENT_TYPE_LOST_LOCK = Constants.FrontendEventType.LOST_LOCK;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({DATA_FORMAT_TS, DATA_FORMAT_PES, DATA_FORMAT_ES, DATA_FORMAT_SHV_TLV})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface DataFormat {}
-
+    /** @hide */
     public static final int DATA_FORMAT_TS = Constants.DataFormat.TS;
+    /** @hide */
     public static final int DATA_FORMAT_PES = Constants.DataFormat.PES;
+    /** @hide */
     public static final int DATA_FORMAT_ES = Constants.DataFormat.ES;
+    /** @hide */
     public static final int DATA_FORMAT_SHV_TLV = Constants.DataFormat.SHV_TLV;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({DEMUX_T_PID, DEMUX_MMPT_PID})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface DemuxPidType {}
-
+    /** @hide */
     public static final int DEMUX_T_PID = 1;
+    /** @hide */
     public static final int DEMUX_MMPT_PID = 2;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({FRONTEND_SETTINGS_ANALOG, FRONTEND_SETTINGS_ATSC, FRONTEND_SETTINGS_ATSC3,
             FRONTEND_SETTINGS_DVBS, FRONTEND_SETTINGS_DVBC, FRONTEND_SETTINGS_DVBT,
             FRONTEND_SETTINGS_ISDBS, FRONTEND_SETTINGS_ISDBS3, FRONTEND_SETTINGS_ISDBT})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendSettingsType {}
-
+    /** @hide */
     public static final int FRONTEND_SETTINGS_ANALOG = 1;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_ATSC = 2;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_ATSC3 = 3;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_DVBS = 4;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_DVBC = 5;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_DVBT = 6;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_ISDBS = 7;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_ISDBS3 = 8;
+    /** @hide */
     public static final int FRONTEND_SETTINGS_ISDBT = 9;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({FILTER_TYPE_TS, FILTER_TYPE_MMTP, FILTER_TYPE_IP, FILTER_TYPE_TLV, FILTER_TYPE_ALP})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FilterType {}
-
+    /** @hide */
     public static final int FILTER_TYPE_TS = Constants.DemuxFilterMainType.TS;
+    /** @hide */
     public static final int FILTER_TYPE_MMTP = Constants.DemuxFilterMainType.MMTP;
+    /** @hide */
     public static final int FILTER_TYPE_IP = Constants.DemuxFilterMainType.IP;
+    /** @hide */
     public static final int FILTER_TYPE_TLV = Constants.DemuxFilterMainType.TLV;
+    /** @hide */
     public static final int FILTER_TYPE_ALP = Constants.DemuxFilterMainType.ALP;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({FILTER_SUBTYPE_UNDEFINED, FILTER_SUBTYPE_SECTION, FILTER_SUBTYPE_PES,
             FILTER_SUBTYPE_AUDIO, FILTER_SUBTYPE_VIDEO, FILTER_SUBTYPE_DOWNLOAD,
             FILTER_SUBTYPE_RECORD, FILTER_SUBTYPE_TS, FILTER_SUBTYPE_PCR, FILTER_SUBTYPE_TEMI,
             FILTER_SUBTYPE_MMPT, FILTER_SUBTYPE_NTP, FILTER_SUBTYPE_IP_PAYLOAD, FILTER_SUBTYPE_IP,
             FILTER_SUBTYPE_PAYLOAD_THROUGH, FILTER_SUBTYPE_TLV, FILTER_SUBTYPE_PTP, })
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FilterSubtype {}
-
+    /** @hide */
     public static final int FILTER_SUBTYPE_UNDEFINED = 0;
+    /** @hide */
     public static final int FILTER_SUBTYPE_SECTION = 1;
+    /** @hide */
     public static final int FILTER_SUBTYPE_PES = 2;
+    /** @hide */
     public static final int FILTER_SUBTYPE_AUDIO = 3;
+    /** @hide */
     public static final int FILTER_SUBTYPE_VIDEO = 4;
+    /** @hide */
     public static final int FILTER_SUBTYPE_DOWNLOAD = 5;
+    /** @hide */
     public static final int FILTER_SUBTYPE_RECORD = 6;
+    /** @hide */
     public static final int FILTER_SUBTYPE_TS = 7;
+    /** @hide */
     public static final int FILTER_SUBTYPE_PCR = 8;
+    /** @hide */
     public static final int FILTER_SUBTYPE_TEMI = 9;
+    /** @hide */
     public static final int FILTER_SUBTYPE_MMPT = 10;
+    /** @hide */
     public static final int FILTER_SUBTYPE_NTP = 11;
+    /** @hide */
     public static final int FILTER_SUBTYPE_IP_PAYLOAD = 12;
+    /** @hide */
     public static final int FILTER_SUBTYPE_IP = 13;
+    /** @hide */
     public static final int FILTER_SUBTYPE_PAYLOAD_THROUGH = 14;
+    /** @hide */
     public static final int FILTER_SUBTYPE_TLV = 15;
+    /** @hide */
     public static final int FILTER_SUBTYPE_PTP = 16;
 
+    /** @hide */
+    @IntDef({FILTER_STATUS_DATA_READY, FILTER_STATUS_LOW_WATER, FILTER_STATUS_HIGH_WATER,
+            FILTER_STATUS_OVERFLOW})
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
-    public @interface FrontendScanType {}
+    public @interface FilterStatus {}
 
+    /**
+     * The status of a filter that the data in the filter buffer is ready to be read.
+     */
+    public static final int FILTER_STATUS_DATA_READY = Constants.DemuxFilterStatus.DATA_READY;
+    /**
+     * The status of a filter that the amount of available data in the filter buffer is at low
+     * level.
+     *
+     * The value is set to 25 percent of the buffer size by default. It can be changed when
+     * configuring the filter.
+     */
+    public static final int FILTER_STATUS_LOW_WATER = Constants.DemuxFilterStatus.LOW_WATER;
+    /**
+     * The status of a filter that the amount of available data in the filter buffer is at high
+     * level.
+     * The value is set to 75 percent of the buffer size by default. It can be changed when
+     * configuring the filter.
+     */
+    public static final int FILTER_STATUS_HIGH_WATER = Constants.DemuxFilterStatus.HIGH_WATER;
+    /**
+     * The status of a filter that the filter buffer is full and newly filtered data is being
+     * discarded.
+     */
+    public static final int FILTER_STATUS_OVERFLOW = Constants.DemuxFilterStatus.OVERFLOW;
+
+    /** @hide */
+    @IntDef({FRONTEND_SCAN_UNDEFINED, FRONTEND_SCAN_AUTO, FRONTEND_SCAN_BLIND})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendScanType {}
+    /** @hide */
     public static final int FRONTEND_SCAN_UNDEFINED = Constants.FrontendScanType.SCAN_UNDEFINED;
+    /** @hide */
     public static final int FRONTEND_SCAN_AUTO = Constants.FrontendScanType.SCAN_AUTO;
+    /** @hide */
     public static final int FRONTEND_SCAN_BLIND = Constants.FrontendScanType.SCAN_BLIND;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({SCAN_MESSAGE_TYPE_LOCKED, SCAN_MESSAGE_TYPE_END, SCAN_MESSAGE_TYPE_PROGRESS_PERCENT,
             SCAN_MESSAGE_TYPE_FREQUENCY, SCAN_MESSAGE_TYPE_SYMBOL_RATE, SCAN_MESSAGE_TYPE_PLP_IDS,
             SCAN_MESSAGE_TYPE_GROUP_IDS, SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS,
             SCAN_MESSAGE_TYPE_STANDARD, SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface ScanMessageType {}
-
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_LOCKED = Constants.FrontendScanMessageType.LOCKED;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_END = Constants.FrontendScanMessageType.END;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_PROGRESS_PERCENT =
             Constants.FrontendScanMessageType.PROGRESS_PERCENT;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_FREQUENCY =
             Constants.FrontendScanMessageType.FREQUENCY;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_SYMBOL_RATE =
             Constants.FrontendScanMessageType.SYMBOL_RATE;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_PLP_IDS = Constants.FrontendScanMessageType.PLP_IDS;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_GROUP_IDS =
             Constants.FrontendScanMessageType.GROUP_IDS;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_INPUT_STREAM_IDS =
             Constants.FrontendScanMessageType.INPUT_STREAM_IDS;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_STANDARD =
             Constants.FrontendScanMessageType.STANDARD;
+    /** @hide */
     public static final int SCAN_MESSAGE_TYPE_ATSC3_PLP_INFO =
             Constants.FrontendScanMessageType.ATSC3_PLP_INFO;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({FRONTEND_STATUS_TYPE_DEMOD_LOCK, FRONTEND_STATUS_TYPE_SNR, FRONTEND_STATUS_TYPE_BER,
             FRONTEND_STATUS_TYPE_PER, FRONTEND_STATUS_TYPE_PRE_BER,
             FRONTEND_STATUS_TYPE_SIGNAL_QUALITY, FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH,
@@ -174,279 +275,342 @@
             FRONTEND_STATUS_TYPE_LBER_CN, FRONTEND_STATUS_TYPE_XER_CN, 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)
     public @interface FrontendStatusType {}
 
     /**
      * Lock status for Demod.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK =
             Constants.FrontendStatusType.DEMOD_LOCK;
     /**
      * Signal to Noise Ratio.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_SNR = Constants.FrontendStatusType.SNR;
     /**
      * Bit Error Ratio.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_BER = Constants.FrontendStatusType.BER;
     /**
      * Packages Error Ratio.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_PER = Constants.FrontendStatusType.PER;
     /**
      * Bit Error Ratio before FEC.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_PRE_BER = Constants.FrontendStatusType.PRE_BER;
     /**
      * Signal Quality (0..100). Good data over total data in percent can be
      * used as a way to present Signal Quality.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_SIGNAL_QUALITY =
             Constants.FrontendStatusType.SIGNAL_QUALITY;
     /**
      * Signal Strength.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH =
             Constants.FrontendStatusType.SIGNAL_STRENGTH;
     /**
      * Symbol Rate.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE =
             Constants.FrontendStatusType.SYMBOL_RATE;
     /**
      * Forward Error Correction Type.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_FEC = Constants.FrontendStatusType.FEC;
     /**
      * Modulation Type.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_MODULATION =
             Constants.FrontendStatusType.MODULATION;
     /**
      * Spectral Inversion Type.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_SPECTRAL = Constants.FrontendStatusType.SPECTRAL;
     /**
      * LNB Voltage.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE =
             Constants.FrontendStatusType.LNB_VOLTAGE;
     /**
      * Physical Layer Pipe ID.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_PLP_ID = Constants.FrontendStatusType.PLP_ID;
     /**
      * Status for Emergency Warning Broadcasting System.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_EWBS = Constants.FrontendStatusType.EWBS;
     /**
      * Automatic Gain Control.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_AGC = Constants.FrontendStatusType.AGC;
     /**
      * Low Noise Amplifier.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_LNA = Constants.FrontendStatusType.LNA;
     /**
      * Error status by layer.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR =
             Constants.FrontendStatusType.LAYER_ERROR;
     /**
      * CN value by VBER.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_VBER_CN = Constants.FrontendStatusType.VBER_CN;
     /**
      * CN value by LBER.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_LBER_CN = Constants.FrontendStatusType.LBER_CN;
     /**
      * CN value by XER.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_XER_CN = Constants.FrontendStatusType.XER_CN;
     /**
      * Moduration Error Ratio.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_MER = Constants.FrontendStatusType.MER;
     /**
      * Difference between tuning frequency and actual locked frequency.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET =
             Constants.FrontendStatusType.FREQ_OFFSET;
     /**
      * Hierarchy for DVBT.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_HIERARCHY = Constants.FrontendStatusType.HIERARCHY;
     /**
      * Lock status for RF.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_RF_LOCK = Constants.FrontendStatusType.RF_LOCK;
     /**
      * PLP information in a frequency band for ATSC3.0 frontend.
+     * @hide
      */
     public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO =
             Constants.FrontendStatusType.ATSC3_PLP_INFO;
 
-
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @LongDef({FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
             FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
             FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20,
             FEC_11_45, FEC_13_18, FEC_13_45, FEC_14_45, FEC_23_36, FEC_25_36, FEC_26_45, FEC_28_45,
             FEC_29_45, FEC_31_45, FEC_32_45, FEC_77_90})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendInnerFec {}
 
     /**
      * FEC not defined
+     * @hide
      */
     public static final long FEC_UNDEFINED = Constants.FrontendInnerFec.FEC_UNDEFINED;
     /**
      * hardware is able to detect and set FEC automatically
+     * @hide
      */
     public static final long FEC_AUTO = Constants.FrontendInnerFec.AUTO;
     /**
      * 1/2 conv. code rate
+     * @hide
      */
     public static final long FEC_1_2 = Constants.FrontendInnerFec.FEC_1_2;
     /**
      * 1/3 conv. code rate
+     * @hide
      */
     public static final long FEC_1_3 = Constants.FrontendInnerFec.FEC_1_3;
     /**
      * 1/4 conv. code rate
+     * @hide
      */
     public static final long FEC_1_4 = Constants.FrontendInnerFec.FEC_1_4;
     /**
      * 1/5 conv. code rate
+     * @hide
      */
     public static final long FEC_1_5 = Constants.FrontendInnerFec.FEC_1_5;
     /**
      * 2/3 conv. code rate
+     * @hide
      */
     public static final long FEC_2_3 = Constants.FrontendInnerFec.FEC_2_3;
     /**
      * 2/5 conv. code rate
+     * @hide
      */
     public static final long FEC_2_5 = Constants.FrontendInnerFec.FEC_2_5;
     /**
      * 2/9 conv. code rate
+     * @hide
      */
     public static final long FEC_2_9 = Constants.FrontendInnerFec.FEC_2_9;
     /**
      * 3/4 conv. code rate
+     * @hide
      */
     public static final long FEC_3_4 = Constants.FrontendInnerFec.FEC_3_4;
     /**
      * 3/5 conv. code rate
+     * @hide
      */
     public static final long FEC_3_5 = Constants.FrontendInnerFec.FEC_3_5;
     /**
      * 4/5 conv. code rate
+     * @hide
      */
     public static final long FEC_4_5 = Constants.FrontendInnerFec.FEC_4_5;
     /**
      * 4/15 conv. code rate
+     * @hide
      */
     public static final long FEC_4_15 = Constants.FrontendInnerFec.FEC_4_15;
     /**
      * 5/6 conv. code rate
+     * @hide
      */
     public static final long FEC_5_6 = Constants.FrontendInnerFec.FEC_5_6;
     /**
      * 5/9 conv. code rate
+     * @hide
      */
     public static final long FEC_5_9 = Constants.FrontendInnerFec.FEC_5_9;
     /**
      * 6/7 conv. code rate
+     * @hide
      */
     public static final long FEC_6_7 = Constants.FrontendInnerFec.FEC_6_7;
     /**
      * 7/8 conv. code rate
+     * @hide
      */
     public static final long FEC_7_8 = Constants.FrontendInnerFec.FEC_7_8;
     /**
      * 7/9 conv. code rate
+     * @hide
      */
     public static final long FEC_7_9 = Constants.FrontendInnerFec.FEC_7_9;
     /**
      * 7/15 conv. code rate
+     * @hide
      */
     public static final long FEC_7_15 = Constants.FrontendInnerFec.FEC_7_15;
     /**
      * 8/9 conv. code rate
+     * @hide
      */
     public static final long FEC_8_9 = Constants.FrontendInnerFec.FEC_8_9;
     /**
      * 8/15 conv. code rate
+     * @hide
      */
     public static final long FEC_8_15 = Constants.FrontendInnerFec.FEC_8_15;
     /**
      * 9/10 conv. code rate
+     * @hide
      */
     public static final long FEC_9_10 = Constants.FrontendInnerFec.FEC_9_10;
     /**
      * 9/20 conv. code rate
+     * @hide
      */
     public static final long FEC_9_20 = Constants.FrontendInnerFec.FEC_9_20;
     /**
      * 11/15 conv. code rate
+     * @hide
      */
     public static final long FEC_11_15 = Constants.FrontendInnerFec.FEC_11_15;
     /**
      * 11/20 conv. code rate
+     * @hide
      */
     public static final long FEC_11_20 = Constants.FrontendInnerFec.FEC_11_20;
     /**
      * 11/45 conv. code rate
+     * @hide
      */
     public static final long FEC_11_45 = Constants.FrontendInnerFec.FEC_11_45;
     /**
      * 13/18 conv. code rate
+     * @hide
      */
     public static final long FEC_13_18 = Constants.FrontendInnerFec.FEC_13_18;
     /**
      * 13/45 conv. code rate
+     * @hide
      */
     public static final long FEC_13_45 = Constants.FrontendInnerFec.FEC_13_45;
     /**
      * 14/45 conv. code rate
+     * @hide
      */
     public static final long FEC_14_45 = Constants.FrontendInnerFec.FEC_14_45;
     /**
      * 23/36 conv. code rate
+     * @hide
      */
     public static final long FEC_23_36 = Constants.FrontendInnerFec.FEC_23_36;
     /**
      * 25/36 conv. code rate
+     * @hide
      */
     public static final long FEC_25_36 = Constants.FrontendInnerFec.FEC_25_36;
     /**
      * 26/45 conv. code rate
+     * @hide
      */
     public static final long FEC_26_45 = Constants.FrontendInnerFec.FEC_26_45;
     /**
      * 28/45 conv. code rate
+     * @hide
      */
     public static final long FEC_28_45 = Constants.FrontendInnerFec.FEC_28_45;
     /**
      * 29/45 conv. code rate
+     * @hide
      */
     public static final long FEC_29_45 = Constants.FrontendInnerFec.FEC_29_45;
     /**
      * 31/45 conv. code rate
+     * @hide
      */
     public static final long FEC_31_45 = Constants.FrontendInnerFec.FEC_31_45;
     /**
      * 32/45 conv. code rate
+     * @hide
      */
     public static final long FEC_32_45 = Constants.FrontendInnerFec.FEC_32_45;
     /**
      * 77/90 conv. code rate
+     * @hide
      */
     public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({DVBC_MODULATION_UNDEFINED, DVBC_MODULATION_AUTO, DVBC_MODULATION_MOD_16QAM,
             DVBC_MODULATION_MOD_32QAM, DVBC_MODULATION_MOD_64QAM, DVBC_MODULATION_MOD_128QAM,
             DVBC_MODULATION_MOD_256QAM, DVBS_MODULATION_UNDEFINED, DVBS_MODULATION_AUTO,
@@ -461,166 +625,756 @@
             ISDBS3_MODULATION_MOD_32APSK, ISDBT_MODULATION_UNDEFINED, ISDBT_MODULATION_AUTO,
             ISDBT_MODULATION_MOD_DQPSK, ISDBT_MODULATION_MOD_QPSK, ISDBT_MODULATION_MOD_16QAM,
             ISDBT_MODULATION_MOD_64QAM})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendModulation {}
-
+    /** @hide */
     public static final int DVBC_MODULATION_UNDEFINED = Constants.FrontendDvbcModulation.UNDEFINED;
+    /** @hide */
     public static final int DVBC_MODULATION_AUTO = Constants.FrontendDvbcModulation.AUTO;
+    /** @hide */
     public static final int DVBC_MODULATION_MOD_16QAM = Constants.FrontendDvbcModulation.MOD_16QAM;
+    /** @hide */
     public static final int DVBC_MODULATION_MOD_32QAM = Constants.FrontendDvbcModulation.MOD_32QAM;
+    /** @hide */
     public static final int DVBC_MODULATION_MOD_64QAM = Constants.FrontendDvbcModulation.MOD_64QAM;
+    /** @hide */
     public static final int DVBC_MODULATION_MOD_128QAM =
             Constants.FrontendDvbcModulation.MOD_128QAM;
+    /** @hide */
     public static final int DVBC_MODULATION_MOD_256QAM =
             Constants.FrontendDvbcModulation.MOD_256QAM;
+    /** @hide */
     public static final int DVBS_MODULATION_UNDEFINED = Constants.FrontendDvbsModulation.UNDEFINED;
+    /** @hide */
     public static final int DVBS_MODULATION_AUTO = Constants.FrontendDvbsModulation.AUTO;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_QPSK = Constants.FrontendDvbsModulation.MOD_QPSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_8PSK = Constants.FrontendDvbsModulation.MOD_8PSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_16QAM = Constants.FrontendDvbsModulation.MOD_16QAM;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_16PSK = Constants.FrontendDvbsModulation.MOD_16PSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_32PSK = Constants.FrontendDvbsModulation.MOD_32PSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_ACM = Constants.FrontendDvbsModulation.MOD_ACM;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_8APSK = Constants.FrontendDvbsModulation.MOD_8APSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_16APSK =
             Constants.FrontendDvbsModulation.MOD_16APSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_32APSK =
             Constants.FrontendDvbsModulation.MOD_32APSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_64APSK =
             Constants.FrontendDvbsModulation.MOD_64APSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_128APSK =
             Constants.FrontendDvbsModulation.MOD_128APSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_256APSK =
             Constants.FrontendDvbsModulation.MOD_256APSK;
+    /** @hide */
     public static final int DVBS_MODULATION_MOD_RESERVED =
             Constants.FrontendDvbsModulation.MOD_RESERVED;
+    /** @hide */
     public static final int ISDBS_MODULATION_UNDEFINED =
             Constants.FrontendIsdbsModulation.UNDEFINED;
+    /** @hide */
     public static final int ISDBS_MODULATION_AUTO = Constants.FrontendIsdbsModulation.AUTO;
+    /** @hide */
     public static final int ISDBS_MODULATION_MOD_BPSK = Constants.FrontendIsdbsModulation.MOD_BPSK;
+    /** @hide */
     public static final int ISDBS_MODULATION_MOD_QPSK = Constants.FrontendIsdbsModulation.MOD_QPSK;
+    /** @hide */
     public static final int ISDBS_MODULATION_MOD_TC8PSK =
             Constants.FrontendIsdbsModulation.MOD_TC8PSK;
+    /** @hide */
     public static final int ISDBS3_MODULATION_UNDEFINED =
             Constants.FrontendIsdbs3Modulation.UNDEFINED;
+    /** @hide */
     public static final int ISDBS3_MODULATION_AUTO = Constants.FrontendIsdbs3Modulation.AUTO;
+    /** @hide */
     public static final int ISDBS3_MODULATION_MOD_BPSK =
             Constants.FrontendIsdbs3Modulation.MOD_BPSK;
+    /** @hide */
     public static final int ISDBS3_MODULATION_MOD_QPSK =
             Constants.FrontendIsdbs3Modulation.MOD_QPSK;
+    /** @hide */
     public static final int ISDBS3_MODULATION_MOD_8PSK =
             Constants.FrontendIsdbs3Modulation.MOD_8PSK;
+    /** @hide */
     public static final int ISDBS3_MODULATION_MOD_16APSK =
             Constants.FrontendIsdbs3Modulation.MOD_16APSK;
+    /** @hide */
     public static final int ISDBS3_MODULATION_MOD_32APSK =
             Constants.FrontendIsdbs3Modulation.MOD_32APSK;
+    /** @hide */
     public static final int ISDBT_MODULATION_UNDEFINED =
             Constants.FrontendIsdbtModulation.UNDEFINED;
+    /** @hide */
     public static final int ISDBT_MODULATION_AUTO = Constants.FrontendIsdbtModulation.AUTO;
+    /** @hide */
     public static final int ISDBT_MODULATION_MOD_DQPSK =
             Constants.FrontendIsdbtModulation.MOD_DQPSK;
+    /** @hide */
     public static final int ISDBT_MODULATION_MOD_QPSK = Constants.FrontendIsdbtModulation.MOD_QPSK;
+    /** @hide */
     public static final int ISDBT_MODULATION_MOD_16QAM =
             Constants.FrontendIsdbtModulation.MOD_16QAM;
+    /** @hide */
     public static final int ISDBT_MODULATION_MOD_64QAM =
             Constants.FrontendIsdbtModulation.MOD_64QAM;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({SPECTRAL_INVERSION_UNDEFINED, SPECTRAL_INVERSION_NORMAL, SPECTRAL_INVERSION_INVERTED})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendDvbcSpectralInversion {}
-
+    /** @hide */
     public static final int SPECTRAL_INVERSION_UNDEFINED =
             Constants.FrontendDvbcSpectralInversion.UNDEFINED;
+    /** @hide */
     public static final int SPECTRAL_INVERSION_NORMAL =
             Constants.FrontendDvbcSpectralInversion.NORMAL;
+    /** @hide */
     public static final int SPECTRAL_INVERSION_INVERTED =
             Constants.FrontendDvbcSpectralInversion.INVERTED;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE,
             HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH,
             HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FrontendDvbtHierarchy {}
-
+    /** @hide */
     public static final int HIERARCHY_UNDEFINED = Constants.FrontendDvbtHierarchy.UNDEFINED;
+    /** @hide */
     public static final int HIERARCHY_AUTO = Constants.FrontendDvbtHierarchy.AUTO;
+    /** @hide */
     public static final int HIERARCHY_NON_NATIVE =
             Constants.FrontendDvbtHierarchy.HIERARCHY_NON_NATIVE;
+    /** @hide */
     public static final int HIERARCHY_1_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_1_NATIVE;
+    /** @hide */
     public static final int HIERARCHY_2_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_2_NATIVE;
+    /** @hide */
     public static final int HIERARCHY_4_NATIVE = Constants.FrontendDvbtHierarchy.HIERARCHY_4_NATIVE;
+    /** @hide */
     public static final int HIERARCHY_NON_INDEPTH =
             Constants.FrontendDvbtHierarchy.HIERARCHY_NON_INDEPTH;
+    /** @hide */
     public static final int HIERARCHY_1_INDEPTH =
             Constants.FrontendDvbtHierarchy.HIERARCHY_1_INDEPTH;
+    /** @hide */
     public static final int HIERARCHY_2_INDEPTH =
             Constants.FrontendDvbtHierarchy.HIERARCHY_2_INDEPTH;
+    /** @hide */
     public static final int HIERARCHY_4_INDEPTH =
             Constants.FrontendDvbtHierarchy.HIERARCHY_4_INDEPTH;
 
-
+    /** @hide */
+    @IntDef({FRONTEND_ANALOG_TYPE_UNDEFINED, FRONTEND_ANALOG_TYPE_PAL, FRONTEND_ANALOG_TYPE_SECAM,
+            FRONTEND_ANALOG_TYPE_NTSC})
     @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAnalogType {}
+    /** @hide */
+    public static final int FRONTEND_ANALOG_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_TYPE_PAL = Constants.FrontendAnalogType.PAL;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_TYPE_SECAM = Constants.FrontendAnalogType.SECAM;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_TYPE_NTSC = Constants.FrontendAnalogType.NTSC;
+
+    /** @hide */
+    @IntDef({FRONTEND_ANALOG_SIF_UNDEFINED, FRONTEND_ANALOG_SIF_BG, FRONTEND_ANALOG_SIF_BG_A2,
+            FRONTEND_ANALOG_SIF_BG_NICAM, FRONTEND_ANALOG_SIF_I, FRONTEND_ANALOG_SIF_DK,
+            FRONTEND_ANALOG_SIF_DK1, FRONTEND_ANALOG_SIF_DK2, FRONTEND_ANALOG_SIF_DK3,
+            FRONTEND_ANALOG_SIF_DK_NICAM, FRONTEND_ANALOG_SIF_L, FRONTEND_ANALOG_SIF_M,
+            FRONTEND_ANALOG_SIF_M_BTSC, FRONTEND_ANALOG_SIF_M_A2, FRONTEND_ANALOG_SIF_M_EIA_J,
+            FRONTEND_ANALOG_SIF_I_NICAM, FRONTEND_ANALOG_SIF_L_NICAM, FRONTEND_ANALOG_SIF_L_PRIME})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAnalogSifStandard {}
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_UNDEFINED =
+            Constants.FrontendAnalogSifStandard.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_BG = Constants.FrontendAnalogSifStandard.BG;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_BG_NICAM =
+            Constants.FrontendAnalogSifStandard.BG_NICAM;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_I = Constants.FrontendAnalogSifStandard.I;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_DK = Constants.FrontendAnalogSifStandard.DK;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_DK_NICAM =
+            Constants.FrontendAnalogSifStandard.DK_NICAM;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_L = Constants.FrontendAnalogSifStandard.L;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_M = Constants.FrontendAnalogSifStandard.M;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_M_EIA_J =
+            Constants.FrontendAnalogSifStandard.M_EIA_J;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_I_NICAM =
+            Constants.FrontendAnalogSifStandard.I_NICAM;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_L_NICAM =
+            Constants.FrontendAnalogSifStandard.L_NICAM;
+    /** @hide */
+    public static final int FRONTEND_ANALOG_SIF_L_PRIME =
+            Constants.FrontendAnalogSifStandard.L_PRIME;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC_MODULATION_UNDEFINED, FRONTEND_ATSC_MODULATION_AUTO,
+            FRONTEND_ATSC_MODULATION_MOD_8VSB, FRONTEND_ATSC_MODULATION_MOD_16VSB})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtscModulation {}
+    /** @hide */
+    public static final int FRONTEND_ATSC_MODULATION_UNDEFINED =
+            Constants.FrontendAtscModulation.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC_MODULATION_AUTO = Constants.FrontendAtscModulation.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ATSC_MODULATION_MOD_8VSB =
+            Constants.FrontendAtscModulation.MOD_8VSB;
+    /** @hide */
+    public static final int FRONTEND_ATSC_MODULATION_MOD_16VSB =
+            Constants.FrontendAtscModulation.MOD_16VSB;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC3_BANDWIDTH_UNDEFINED, FRONTEND_ATSC3_BANDWIDTH_AUTO,
+            FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_6MHZ, FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_7MHZ,
+            FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_8MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtsc3Bandwidth {}
+    /** @hide */
+    public static final int FRONTEND_ATSC3_BANDWIDTH_UNDEFINED =
+            Constants.FrontendAtsc3Bandwidth.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_BANDWIDTH_AUTO = Constants.FrontendAtsc3Bandwidth.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_6MHZ =
+            Constants.FrontendAtsc3Bandwidth.BANDWIDTH_6MHZ;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_7MHZ =
+            Constants.FrontendAtsc3Bandwidth.BANDWIDTH_7MHZ;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_8MHZ =
+            Constants.FrontendAtsc3Bandwidth.BANDWIDTH_8MHZ;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC3_MODULATION_UNDEFINED, FRONTEND_ATSC3_MODULATION_AUTO,
+            FRONTEND_ATSC3_MODULATION_MOD_QPSK, FRONTEND_ATSC3_MODULATION_MOD_16QAM,
+            FRONTEND_ATSC3_MODULATION_MOD_64QAM, FRONTEND_ATSC3_MODULATION_MOD_256QAM,
+            FRONTEND_ATSC3_MODULATION_MOD_1024QAM, FRONTEND_ATSC3_MODULATION_MOD_4096QAM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtsc3Modulation {}
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_UNDEFINED =
+            Constants.FrontendAtsc3Modulation.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_AUTO = Constants.FrontendAtsc3Modulation.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_QPSK =
+            Constants.FrontendAtsc3Modulation.MOD_QPSK;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_16QAM =
+            Constants.FrontendAtsc3Modulation.MOD_16QAM;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_64QAM =
+            Constants.FrontendAtsc3Modulation.MOD_64QAM;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_256QAM =
+            Constants.FrontendAtsc3Modulation.MOD_256QAM;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_1024QAM =
+            Constants.FrontendAtsc3Modulation.MOD_1024QAM;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_4096QAM =
+            Constants.FrontendAtsc3Modulation.MOD_4096QAM;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_UNDEFINED,
+            FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_AUTO, FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_CTI,
+            FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_HTI})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtsc3TimeInterleaveMode {}
+    /** @hide */
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_UNDEFINED =
+            Constants.FrontendAtsc3TimeInterleaveMode.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_AUTO =
+            Constants.FrontendAtsc3TimeInterleaveMode.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_CTI =
+            Constants.FrontendAtsc3TimeInterleaveMode.CTI;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_HTI =
+            Constants.FrontendAtsc3TimeInterleaveMode.HTI;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC3_CODERATE_UNDEFINED, FRONTEND_ATSC3_CODERATE_AUTO,
+            FRONTEND_ATSC3_CODERATE_2_15, FRONTEND_ATSC3_CODERATE_3_15,
+            FRONTEND_ATSC3_CODERATE_4_15, FRONTEND_ATSC3_CODERATE_5_15,
+            FRONTEND_ATSC3_CODERATE_6_15, FRONTEND_ATSC3_CODERATE_7_15,
+            FRONTEND_ATSC3_CODERATE_8_15, FRONTEND_ATSC3_CODERATE_9_15,
+            FRONTEND_ATSC3_CODERATE_10_15, FRONTEND_ATSC3_CODERATE_11_15,
+            FRONTEND_ATSC3_CODERATE_12_15, FRONTEND_ATSC3_CODERATE_13_15})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtsc3CodeRate {}
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_UNDEFINED =
+            Constants.FrontendAtsc3CodeRate.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_AUTO = Constants.FrontendAtsc3CodeRate.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_2_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_2_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_3_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_3_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_4_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_4_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_5_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_5_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_6_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_6_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_7_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_7_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_8_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_8_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_9_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_9_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_10_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_10_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_11_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_11_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_12_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_12_15;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_CODERATE_13_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_13_15;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC3_FEC_UNDEFINED, FRONTEND_ATSC3_FEC_AUTO, FRONTEND_ATSC3_FEC_BCH_LDPC_16K,
+            FRONTEND_ATSC3_FEC_BCH_LDPC_64K, FRONTEND_ATSC3_FEC_CRC_LDPC_16K,
+            FRONTEND_ATSC3_FEC_CRC_LDPC_64K, FRONTEND_ATSC3_FEC_LDPC_16K,
+            FRONTEND_ATSC3_FEC_LDPC_64K})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtsc3Fec {}
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_UNDEFINED = Constants.FrontendAtsc3Fec.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_AUTO = Constants.FrontendAtsc3Fec.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_BCH_LDPC_16K =
+            Constants.FrontendAtsc3Fec.BCH_LDPC_16K;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_BCH_LDPC_64K =
+            Constants.FrontendAtsc3Fec.BCH_LDPC_64K;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_CRC_LDPC_16K =
+            Constants.FrontendAtsc3Fec.CRC_LDPC_16K;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_CRC_LDPC_64K =
+            Constants.FrontendAtsc3Fec.CRC_LDPC_64K;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_LDPC_16K = Constants.FrontendAtsc3Fec.LDPC_16K;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_FEC_LDPC_64K = Constants.FrontendAtsc3Fec.LDPC_64K;
+
+    /** @hide */
+    @IntDef({FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_UNDEFINED,
+            FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET,
+            FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendAtsc3DemodOutputFormat {}
+    /** @hide */
+    public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_UNDEFINED =
+            Constants.FrontendAtsc3DemodOutputFormat.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET =
+            Constants.FrontendAtsc3DemodOutputFormat.ATSC3_LINKLAYER_PACKET;
+    /** @hide */
+    public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET =
+            Constants.FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBS_STANDARD_AUTO, FRONTEND_DVBS_STANDARD_S, FRONTEND_DVBS_STANDARD_S2,
+            FRONTEND_DVBS_STANDARD_S2X})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbsStandard {}
+    /** @hide */
+    public static final int FRONTEND_DVBS_STANDARD_AUTO = Constants.FrontendDvbsStandard.AUTO;
+    /** @hide */
+    public static final int FRONTEND_DVBS_STANDARD_S = Constants.FrontendDvbsStandard.S;
+    /** @hide */
+    public static final int FRONTEND_DVBS_STANDARD_S2 = Constants.FrontendDvbsStandard.S2;
+    /** @hide */
+    public static final int FRONTEND_DVBS_STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBC_ANNEX_UNDEFINED, FRONTEND_DVBC_ANNEX_A, FRONTEND_DVBC_ANNEX_B,
+            FRONTEND_DVBC_ANNEX_C})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbcAnnex {}
+    /** @hide */
+    public static final int FRONTEND_DVBC_ANNEX_UNDEFINED = Constants.FrontendDvbcAnnex.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_DVBC_ANNEX_A = Constants.FrontendDvbcAnnex.A;
+    /** @hide */
+    public static final int FRONTEND_DVBC_ANNEX_B = Constants.FrontendDvbcAnnex.B;
+    /** @hide */
+    public static final int FRONTEND_DVBC_ANNEX_C = Constants.FrontendDvbcAnnex.C;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBT_TRANSMISSION_MODE_UNDEFINED, FRONTEND_DVBT_TRANSMISSION_MODE_AUTO,
+            FRONTEND_DVBT_TRANSMISSION_MODE_2K, FRONTEND_DVBT_TRANSMISSION_MODE_8K,
+            FRONTEND_DVBT_TRANSMISSION_MODE_4K, FRONTEND_DVBT_TRANSMISSION_MODE_1K,
+            FRONTEND_DVBT_TRANSMISSION_MODE_16K, FRONTEND_DVBT_TRANSMISSION_MODE_32K})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbtTransmissionMode {}
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_UNDEFINED =
+            Constants.FrontendDvbtTransmissionMode.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_AUTO =
+            Constants.FrontendDvbtTransmissionMode.AUTO;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_2K =
+            Constants.FrontendDvbtTransmissionMode.MODE_2K;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_8K =
+            Constants.FrontendDvbtTransmissionMode.MODE_8K;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_4K =
+            Constants.FrontendDvbtTransmissionMode.MODE_4K;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_1K =
+            Constants.FrontendDvbtTransmissionMode.MODE_1K;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_16K =
+            Constants.FrontendDvbtTransmissionMode.MODE_16K;
+    /** @hide */
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_32K =
+            Constants.FrontendDvbtTransmissionMode.MODE_32K;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBT_BANDWIDTH_UNDEFINED, FRONTEND_DVBT_BANDWIDTH_AUTO,
+            FRONTEND_DVBT_BANDWIDTH_8MHZ, FRONTEND_DVBT_BANDWIDTH_7MHZ,
+            FRONTEND_DVBT_BANDWIDTH_6MHZ, FRONTEND_DVBT_BANDWIDTH_5MHZ,
+            FRONTEND_DVBT_BANDWIDTH_1_7MHZ, FRONTEND_DVBT_BANDWIDTH_10MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbtBandwidth {}
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_UNDEFINED =
+            Constants.FrontendDvbtBandwidth.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_AUTO = Constants.FrontendDvbtBandwidth.AUTO;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_8MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_8MHZ;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_7MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_7MHZ;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_6MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_6MHZ;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_5MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_1_7MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
+    /** @hide */
+    public static final int FRONTEND_DVBT_BANDWIDTH_10MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_10MHZ;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBT_CONSTELLATION_UNDEFINED, FRONTEND_DVBT_CONSTELLATION_AUTO,
+            FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_QPSK,
+            FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_16QAM,
+            FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_64QAM,
+            FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_256QAM})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbtConstellation {}
+    /** @hide */
+    public static final int FRONTEND_DVBT_CONSTELLATION_UNDEFINED =
+            Constants.FrontendDvbtConstellation.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CONSTELLATION_AUTO =
+            Constants.FrontendDvbtConstellation.AUTO;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_QPSK =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_16QAM =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_64QAM =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_256QAM =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBT_CODERATE_UNDEFINED, FRONTEND_DVBT_CODERATE_AUTO,
+            FRONTEND_DVBT_CODERATE_1_2, FRONTEND_DVBT_CODERATE_2_3, FRONTEND_DVBT_CODERATE_3_4,
+            FRONTEND_DVBT_CODERATE_5_6, FRONTEND_DVBT_CODERATE_7_8, FRONTEND_DVBT_CODERATE_3_5,
+            FRONTEND_DVBT_CODERATE_4_5, FRONTEND_DVBT_CODERATE_6_7, FRONTEND_DVBT_CODERATE_8_9})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbtCoderate {}
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_UNDEFINED =
+            Constants.FrontendDvbtCoderate.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_1_2 =
+            Constants.FrontendDvbtCoderate.CODERATE_1_2;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_2_3 =
+            Constants.FrontendDvbtCoderate.CODERATE_2_3;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_3_4 =
+            Constants.FrontendDvbtCoderate.CODERATE_3_4;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_5_6 =
+            Constants.FrontendDvbtCoderate.CODERATE_5_6;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_7_8 =
+            Constants.FrontendDvbtCoderate.CODERATE_7_8;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_3_5 =
+            Constants.FrontendDvbtCoderate.CODERATE_3_5;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_4_5 =
+            Constants.FrontendDvbtCoderate.CODERATE_4_5;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_6_7 =
+            Constants.FrontendDvbtCoderate.CODERATE_6_7;
+    /** @hide */
+    public static final int FRONTEND_DVBT_CODERATE_8_9 =
+            Constants.FrontendDvbtCoderate.CODERATE_8_9;
+
+    /** @hide */
+    @IntDef({FRONTEND_DVBT_GUARD_INTERVAL_UNDEFINED, FRONTEND_DVBT_GUARD_INTERVAL_AUTO,
+            FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_32, FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_16,
+            FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_8, FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_4,
+            FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_128,
+            FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_128,
+            FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_256})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendDvbtGuardInterval {}
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_UNDEFINED =
+            Constants.FrontendDvbtGuardInterval.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_AUTO =
+            Constants.FrontendDvbtGuardInterval.AUTO;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_32 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_32;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_16 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_16;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_8 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_8;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_4 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_4;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_128 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_128;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_128 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_19_128;
+    /** @hide */
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_256 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
+
+    /** @hide */
+    @IntDef({FRONTEND_ISDBS_CODERATE_UNDEFINED, FRONTEND_ISDBS_CODERATE_AUTO,
+            FRONTEND_ISDBS_CODERATE_1_2, FRONTEND_ISDBS_CODERATE_2_3, FRONTEND_ISDBS_CODERATE_3_4,
+            FRONTEND_ISDBS_CODERATE_5_6, FRONTEND_ISDBS_CODERATE_7_8})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendIsdbsCoderate {}
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_UNDEFINED =
+            Constants.FrontendIsdbsCoderate.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_1_2 =
+            Constants.FrontendIsdbsCoderate.CODERATE_1_2;
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_2_3 =
+            Constants.FrontendIsdbsCoderate.CODERATE_2_3;
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_3_4 =
+            Constants.FrontendIsdbsCoderate.CODERATE_3_4;
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_5_6 =
+            Constants.FrontendIsdbsCoderate.CODERATE_5_6;
+    /** @hide */
+    public static final int FRONTEND_ISDBS_CODERATE_7_8 =
+            Constants.FrontendIsdbsCoderate.CODERATE_7_8;
+
+    /** @hide */
+    @IntDef({FRONTEND_ISDBT_MODE_UNDEFINED, FRONTEND_ISDBT_MODE_AUTO, FRONTEND_ISDBT_MODE_1,
+            FRONTEND_ISDBT_MODE_2, FRONTEND_ISDBT_MODE_3})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendIsdbtMode {}
+    /** @hide */
+    public static final int FRONTEND_ISDBT_MODE_UNDEFINED = Constants.FrontendIsdbtMode.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_MODE_AUTO = Constants.FrontendIsdbtMode.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_MODE_1 = Constants.FrontendIsdbtMode.MODE_1;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_MODE_2 = Constants.FrontendIsdbtMode.MODE_2;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_MODE_3 = Constants.FrontendIsdbtMode.MODE_3;
+
+    /** @hide */
+    @IntDef({FRONTEND_ISDBT_BANDWIDTH_UNDEFINED, FRONTEND_ISDBT_BANDWIDTH_AUTO,
+            FRONTEND_ISDBT_BANDWIDTH_8MHZ, FRONTEND_ISDBT_BANDWIDTH_7MHZ,
+            FRONTEND_ISDBT_BANDWIDTH_6MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FrontendIsdbtBandwidth {}
+    /** @hide */
+    public static final int FRONTEND_ISDBT_BANDWIDTH_UNDEFINED =
+            Constants.FrontendIsdbtBandwidth.UNDEFINED;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_BANDWIDTH_AUTO = Constants.FrontendIsdbtBandwidth.AUTO;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_BANDWIDTH_8MHZ =
+            Constants.FrontendIsdbtBandwidth.BANDWIDTH_8MHZ;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_BANDWIDTH_7MHZ =
+            Constants.FrontendIsdbtBandwidth.BANDWIDTH_7MHZ;
+    /** @hide */
+    public static final int FRONTEND_ISDBT_BANDWIDTH_6MHZ =
+            Constants.FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
+
+    /** @hide */
     @IntDef({FILTER_SETTINGS_TS, FILTER_SETTINGS_MMTP, FILTER_SETTINGS_IP, FILTER_SETTINGS_TLV,
             FILTER_SETTINGS_ALP})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface FilterSettingsType {}
-
+    /** @hide */
     public static final int FILTER_SETTINGS_TS = Constants.DemuxFilterMainType.TS;
+    /** @hide */
     public static final int FILTER_SETTINGS_MMTP = Constants.DemuxFilterMainType.MMTP;
+    /** @hide */
     public static final int FILTER_SETTINGS_IP = Constants.DemuxFilterMainType.IP;
+    /** @hide */
     public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
+    /** @hide */
     public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface DvrSettingsType {}
-
+    /** @hide */
     public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
+    /** @hide */
     public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({LNB_VOLTAGE_NONE, LNB_VOLTAGE_5V, LNB_VOLTAGE_11V, LNB_VOLTAGE_12V, LNB_VOLTAGE_13V,
             LNB_VOLTAGE_14V, LNB_VOLTAGE_15V, LNB_VOLTAGE_18V, LNB_VOLTAGE_19V})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface LnbVoltage {}
-
+    /** @hide */
     public static final int LNB_VOLTAGE_NONE = Constants.LnbVoltage.NONE;
+    /** @hide */
     public static final int LNB_VOLTAGE_5V = Constants.LnbVoltage.VOLTAGE_5V;
+    /** @hide */
     public static final int LNB_VOLTAGE_11V = Constants.LnbVoltage.VOLTAGE_11V;
+    /** @hide */
     public static final int LNB_VOLTAGE_12V = Constants.LnbVoltage.VOLTAGE_12V;
+    /** @hide */
     public static final int LNB_VOLTAGE_13V = Constants.LnbVoltage.VOLTAGE_13V;
+    /** @hide */
     public static final int LNB_VOLTAGE_14V = Constants.LnbVoltage.VOLTAGE_14V;
+    /** @hide */
     public static final int LNB_VOLTAGE_15V = Constants.LnbVoltage.VOLTAGE_15V;
+    /** @hide */
     public static final int LNB_VOLTAGE_18V = Constants.LnbVoltage.VOLTAGE_18V;
+    /** @hide */
     public static final int LNB_VOLTAGE_19V = Constants.LnbVoltage.VOLTAGE_19V;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({LNB_TONE_NONE, LNB_TONE_CONTINUOUS})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface LnbTone {}
-
+    /** @hide */
     public static final int LNB_TONE_NONE = Constants.LnbTone.NONE;
+    /** @hide */
     public static final int LNB_TONE_CONTINUOUS = Constants.LnbTone.CONTINUOUS;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({LNB_POSITION_UNDEFINED, LNB_POSITION_A, LNB_POSITION_B})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface LnbPosition {}
-
+    /** @hide */
     public static final int LNB_POSITION_UNDEFINED = Constants.LnbPosition.UNDEFINED;
+    /** @hide */
     public static final int LNB_POSITION_A = Constants.LnbPosition.POSITION_A;
+    /** @hide */
     public static final int LNB_POSITION_B = Constants.LnbPosition.POSITION_B;
 
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @hide */
     @IntDef({RESULT_SUCCESS, RESULT_UNAVAILABLE, RESULT_NOT_INITIALIZED, RESULT_INVALID_STATE,
             RESULT_INVALID_ARGUMENT, RESULT_OUT_OF_MEMORY, RESULT_UNKNOWN_ERROR})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface Result {}
-
+    /** @hide */
     public static final int RESULT_SUCCESS = Constants.Result.SUCCESS;
+    /** @hide */
     public static final int RESULT_UNAVAILABLE = Constants.Result.UNAVAILABLE;
+    /** @hide */
     public static final int RESULT_NOT_INITIALIZED = Constants.Result.NOT_INITIALIZED;
+    /** @hide */
     public static final int RESULT_INVALID_STATE = Constants.Result.INVALID_STATE;
+    /** @hide */
     public static final int RESULT_INVALID_ARGUMENT = Constants.Result.INVALID_ARGUMENT;
+    /** @hide */
     public static final int RESULT_OUT_OF_MEMORY = Constants.Result.OUT_OF_MEMORY;
+    /** @hide */
     public static final int RESULT_UNKNOWN_ERROR = Constants.Result.UNKNOWN_ERROR;
 
     private TunerConstants() {
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index b3bd780..a7ccb02 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -30,14 +30,26 @@
 public final class TunerUtils {
     private static final String PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER;
 
-    static void checkTunerPermission(Context context) {
+    /**
+     * Checks whether the caller has permission to access tuner.
+     *
+     * @param context context of the caller.
+     * @throws SecurityException if the caller doesn't have the permission.
+     */
+    public static void checkTunerPermission(Context context) {
         if (context.checkCallingOrSelfPermission(PERMISSION)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Caller must have " + PERMISSION + " permission.");
         }
     }
 
-    static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
+    /**
+     * Gets the corresponding filter subtype constant defined in tuner HAL.
+     *
+     * @param mainType filter main type.
+     * @param subtype filter subtype.
+     */
+    public static int getFilterSubtype(@FilterType int mainType, @FilterSubtype int subtype) {
         if (mainType == TunerConstants.FILTER_TYPE_TS) {
             switch (subtype) {
                 case TunerConstants.FILTER_SUBTYPE_UNDEFINED:
diff --git a/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java b/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java
new file mode 100644
index 0000000..306de84
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/AudioExtraMetaData.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * Extra Meta Data from AD (Audio Descriptor) according to
+ * ETSI TS 101 154 V2.1.1.
+ * @hide
+ */
+public class AudioExtraMetaData {
+    private byte mAdFade;
+    private byte mAdPan;
+    private byte mVersionTextTag;
+    private byte mAdGainCenter;
+    private byte mAdGainFront;
+    private byte mAdGainSurround;
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
similarity index 61%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/DownloadEvent.java
index 1fefda4..548fa77 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,17 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * Download event.
+ * @hide
+ */
+public class DownloadEvent extends FilterEvent {
+    private int mItemId;
+    private int mMpuSequenceNumber;
+    private int mItemFragmentIndex;
+    private int mLastItemFragmentIndex;
+    private int mDataLength;
+}
+
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/FilterEvent.java
similarity index 68%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/FilterEvent.java
index 1fefda4..56a77d4 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/FilterEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+import android.annotation.SystemApi;
+
+/**
+ * An entity class that is passed to the filter callbacks.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class FilterEvent {
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
similarity index 72%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
index 1fefda4..4da1d21 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * IP payload event.
+ * @hide
+ */
+public class IpPayloadEvent extends FilterEvent {
+    private int mDataLength;
+}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
new file mode 100644
index 0000000..7703248
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.os.NativeHandle;
+
+/**
+ * Media event.
+ * @hide
+ */
+public class MediaEvent extends FilterEvent {
+    private int mStreamId;
+    private boolean mIsPtsPresent;
+    private long mPts;
+    private int mDataLength;
+    private NativeHandle mHandle;
+    private boolean mIsSecureMemory;
+    private int mMpuSequenceNumber;
+    private boolean mIsPrivateData;
+    private AudioExtraMetaData mExtraMetaData;
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
similarity index 69%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 1fefda4..dbd8c77 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * MMPT record event.
+ * @hide
+ */
+public class MmtpRecordEvent extends FilterEvent {
+    private int mScHevcIndexMask;
+    private long mByteNumber;
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/PesEvent.java
similarity index 68%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/PesEvent.java
index 1fefda4..16536e2 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/PesEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,14 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * PES event.
+ * @hide
+ */
+public class PesEvent extends FilterEvent {
+    private int mStreamId;
+    private int mDataLength;
+    private int mMpuSequenceNumber;
+}
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
new file mode 100644
index 0000000..e211dda
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+import android.annotation.SystemApi;
+import android.media.tv.tuner.Tuner.Filter;
+
+/**
+ * Filter event sent from {@link Filter} objects with section type.
+ *
+ * @hide
+ */
+@SystemApi
+public class SectionEvent extends FilterEvent {
+    private final int mTableId;
+    private final int mVersion;
+    private final int mSectionNum;
+    private final int mDataLength;
+
+    // This constructor is used by JNI code only
+    private SectionEvent(int tableId, int version, int sectionNum, int dataLength) {
+        mTableId = tableId;
+        mVersion = version;
+        mSectionNum = sectionNum;
+        mDataLength = dataLength;
+    }
+
+    /**
+     * Gets table ID of filtered data.
+     */
+    public int getTableId() {
+        return mTableId;
+    }
+
+    /**
+     * Gets version number of filtered data.
+     */
+    public int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Gets section number of filtered data.
+     */
+    public int getSectionNumber() {
+        return mSectionNum;
+    }
+
+    /**
+     * Gets data size in bytes of filtered data.
+     */
+    public int getDataLength() {
+        return mDataLength;
+    }
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/TemiEvent.java
similarity index 68%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/TemiEvent.java
index 1fefda4..3841604 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/TemiEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,14 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * TEMI event.
+ * @hide
+ */
+public class TemiEvent extends FilterEvent {
+    private long mPts;
+    private byte mDescrTag;
+    private byte[] mDescrData;
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
similarity index 68%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index 1fefda4..875b5bd 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,14 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.filter;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * TS record event.
+ * @hide
+ */
+public class TsRecordEvent extends FilterEvent {
+    private int mTpid;
+    private int mIndexMask;
+    private long mByteNumber;
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
new file mode 100644
index 0000000..16308ce
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for analog.
+ * @hide
+ */
+public class AnalogFrontendSettings extends FrontendSettings {
+    private int mAnalogType;
+    private int mSifStandard;
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_ANALOG;
+    }
+
+    public int getAnalogType() {
+        return mAnalogType;
+    }
+
+    public int getSifStandard() {
+        return mSifStandard;
+    }
+
+    /**
+     * Creates a new builder object.
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    private AnalogFrontendSettings(int frequency, int analogType, int sifStandard) {
+        super(frequency);
+        mAnalogType = analogType;
+        mSifStandard = sifStandard;
+    }
+
+    /**
+     * Builder for FrontendAnalogSettings.
+     */
+    public static class Builder {
+        private int mFrequency;
+        private int mAnalogType;
+        private int mSifStandard;
+
+        private Builder() {}
+
+        /**
+         * Sets frequency.
+         */
+        public Builder setFrequency(int frequency) {
+            mFrequency = frequency;
+            return this;
+        }
+
+        /**
+         * Sets analog type.
+         */
+        public Builder setAnalogType(int analogType) {
+            mAnalogType = analogType;
+            return this;
+        }
+
+        /**
+         * Sets sif standard.
+         */
+        public Builder setSifStandard(int sifStandard) {
+            mSifStandard = sifStandard;
+            return this;
+        }
+
+        /**
+         * Builds a FrontendAnalogSettings instance.
+         */
+        public AnalogFrontendSettings build() {
+            return new AnalogFrontendSettings(mFrequency, mAnalogType, mSifStandard);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
new file mode 100644
index 0000000..bce8a64
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+import java.util.List;
+
+/**
+ * Frontend settings for ATSC-3.
+ * @hide
+ */
+public class Atsc3FrontendSettings extends FrontendSettings {
+    public int bandwidth;
+    public byte demodOutputFormat;
+    public List<Atsc3PlpSettings> plpSettings;
+
+    Atsc3FrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_ATSC3;
+    }
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
similarity index 65%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
index 1fefda4..61c6fec 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3PlpSettings.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,16 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.frontend;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * PLP settings for ATSC-3.
+ * @hide
+ */
+public class Atsc3PlpSettings {
+    public byte plpId;
+    public int modulation;
+    public int interleaveMode;
+    public int codeRate;
+    public int fec;
+}
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
new file mode 100644
index 0000000..14c5cdd
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ATSC.
+ * @hide
+ */
+public class AtscFrontendSettings extends FrontendSettings {
+    public int modulation;
+
+    AtscFrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_ATSC;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
new file mode 100644
index 0000000..07e49ff
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for DVBC.
+ * @hide
+ */
+public class DvbcFrontendSettings extends FrontendSettings {
+    public int modulation;
+    public long fec;
+    public int symbolRate;
+    public int outerFec;
+    public byte annex;
+    public int spectralInversion;
+
+    DvbcFrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_DVBC;
+    }
+}
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
similarity index 66%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
index 1fefda4..bfa4391 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/media/java/android/media/tv/tuner/frontend/DvbsCodeRate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package android.media.tv.tuner.frontend;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+/**
+ * Code rate for DVBS.
+ * @hide
+ */
+public class DvbsCodeRate {
+    public long fec;
+    public boolean isLinear;
+    public boolean isShortFrames;
+    public int bitsPer1000Symbol;
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
new file mode 100644
index 0000000..23c0a7b1
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for DVBS.
+ * @hide
+ */
+public class DvbsFrontendSettings extends FrontendSettings {
+    public int modulation;
+    public DvbsCodeRate coderate;
+    public int symbolRate;
+    public int rolloff;
+    public int pilot;
+    public int inputStreamId;
+    public byte standard;
+
+    DvbsFrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_DVBS;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
new file mode 100644
index 0000000..eec00f3
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for DVBT.
+ * @hide
+ */
+public class DvbtFrontendSettings extends FrontendSettings {
+    public int transmissionMode;
+    public int bandwidth;
+    public int constellation;
+    public int hierarchy;
+    public int hpCoderate;
+    public int lpCoderate;
+    public int guardInterval;
+    public boolean isHighPriority;
+    public byte standard;
+    public boolean isMiso;
+    public int plpMode;
+    public byte plpId;
+    public byte plpGroupId;
+
+    DvbtFrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_DVBT;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendCallback.java b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
new file mode 100644
index 0000000..91776e1
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendCallback.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.ScanMessage;
+
+/**
+ * Frontend Callback.
+ *
+ * @hide
+ */
+public interface FrontendCallback {
+
+    /**
+     * Invoked when there is a frontend event.
+     */
+    void onEvent(int frontendEventType);
+
+    /**
+     * Invoked when there is a scan message.
+     * @param msg
+     */
+    void onScanMessage(ScanMessage msg);
+}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
new file mode 100644
index 0000000..ef6c029
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendCapabilities;
+import android.media.tv.tuner.TunerConstants.FrontendType;
+
+/**
+ * Frontend info.
+ * @hide
+ */
+public class FrontendInfo {
+    private final int mId;
+    private final int mType;
+    private final int mMinFrequency;
+    private final int mMaxFrequency;
+    private final int mMinSymbolRate;
+    private final int mMaxSymbolRate;
+    private final int mAcquireRange;
+    private final int mExclusiveGroupId;
+    private final int[] mStatusCaps;
+    private final FrontendCapabilities mFrontendCap;
+
+    FrontendInfo(int id, int type, int minFrequency, int maxFrequency, int minSymbolRate,
+            int maxSymbolRate, int acquireRange, int exclusiveGroupId, int[] statusCaps,
+            FrontendCapabilities frontendCap) {
+        mId = id;
+        mType = type;
+        mMinFrequency = minFrequency;
+        mMaxFrequency = maxFrequency;
+        mMinSymbolRate = minSymbolRate;
+        mMaxSymbolRate = maxSymbolRate;
+        mAcquireRange = acquireRange;
+        mExclusiveGroupId = exclusiveGroupId;
+        mStatusCaps = statusCaps;
+        mFrontendCap = frontendCap;
+    }
+
+    /** Gets frontend ID. */
+    public int getId() {
+        return mId;
+    }
+    /** Gets frontend type. */
+    @FrontendType
+    public int getType() {
+        return mType;
+    }
+    /** Gets min frequency. */
+    public int getMinFrequency() {
+        return mMinFrequency;
+    }
+    /** Gets max frequency. */
+    public int getMaxFrequency() {
+        return mMaxFrequency;
+    }
+    /** Gets min symbol rate. */
+    public int getMinSymbolRate() {
+        return mMinSymbolRate;
+    }
+    /** Gets max symbol rate. */
+    public int getMaxSymbolRate() {
+        return mMaxSymbolRate;
+    }
+    /** Gets acquire range. */
+    public int getAcquireRange() {
+        return mAcquireRange;
+    }
+    /** Gets exclusive group ID. */
+    public int getExclusiveGroupId() {
+        return mExclusiveGroupId;
+    }
+    /** Gets status capabilities. */
+    public int[] getStatusCapabilities() {
+        return mStatusCaps;
+    }
+    /** Gets frontend capability. */
+    public FrontendCapabilities getFrontendCapability() {
+        return mFrontendCap;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
similarity index 98%
rename from media/java/android/media/tv/tuner/FrontendStatus.java
rename to media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index f8b2d12..89ec536 100644
--- a/media/java/android/media/tv/tuner/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package android.media.tv.tuner;
+package android.media.tv.tuner.frontend;
 
+import android.media.tv.tuner.TunerConstants;
 import android.media.tv.tuner.TunerConstants.FrontendDvbcSpectralInversion;
 import android.media.tv.tuner.TunerConstants.FrontendDvbtHierarchy;
 import android.media.tv.tuner.TunerConstants.FrontendInnerFec;
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
new file mode 100644
index 0000000..736d0b1
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ISDBS-3.
+ * @hide
+ */
+public class Isdbs3FrontendSettings extends FrontendSettings {
+    public int streamId;
+    public int streamIdType;
+    public int modulation;
+    public int coderate;
+    public int symbolRate;
+    public int rolloff;
+
+    Isdbs3FrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_ISDBS3;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
new file mode 100644
index 0000000..7fd5da7
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ISDBS.
+ * @hide
+ */
+public class IsdbsFrontendSettings extends FrontendSettings {
+    public int streamId;
+    public int streamIdType;
+    public int modulation;
+    public int coderate;
+    public int symbolRate;
+    public int rolloff;
+
+    IsdbsFrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_ISDBS;
+    }
+}
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
new file mode 100644
index 0000000..3f83267
--- /dev/null
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.frontend;
+
+
+import android.media.tv.tuner.FrontendSettings;
+import android.media.tv.tuner.TunerConstants;
+
+/**
+ * Frontend settings for ISDBT.
+ * @hide
+ */
+public class IsdbtFrontendSettings extends FrontendSettings {
+    public int modulation;
+    public int bandwidth;
+    public int coderate;
+    public int guardInterval;
+    public int serviceAreaId;
+
+    IsdbtFrontendSettings(int frequency) {
+        super(frequency);
+    }
+
+    @Override
+    public int getType() {
+        return TunerConstants.FRONTEND_TYPE_ISDBT;
+    }
+}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 4ca23a1..ee67613 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -139,6 +139,7 @@
         "libfmq",
         "libhidlbase",
         "liblog",
+        "libnativehelper",
         "libutils",
     ],
 
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index ef806e4..f0f3688 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -22,6 +22,7 @@
 
 #include <android/hardware/tv/tuner/1.0/ITuner.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <nativehelper/JNIHelp.h>
 
 #pragma GCC diagnostic ignored "-Wunused-function"
 
@@ -92,19 +93,35 @@
 }
 
 void DvrCallback::setDvr(const jobject dvr) {
-    ALOGD("FilterCallback::setDvr");
+    ALOGD("DvrCallback::setDvr");
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     mDvr = env->NewWeakGlobalRef(dvr);
 }
 
 /////////////// Dvr ///////////////////////
 
-Dvr::Dvr(sp<IDvr> sp, jweak obj) : mDvrSp(sp), mDvrObj(obj) {}
+Dvr::Dvr(sp<IDvr> sp, jweak obj) : mDvrSp(sp), mDvrObj(obj), mDvrMQEventFlag(nullptr) {}
+
+Dvr::~Dvr() {
+    EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+}
+
+int Dvr::close() {
+    Result r = mDvrSp->close();
+    if (r == Result::SUCCESS) {
+        EventFlag::deleteEventFlag(&mDvrMQEventFlag);
+    }
+    return (int)r;
+}
 
 sp<IDvr> Dvr::getIDvr() {
     return mDvrSp;
 }
 
+DvrMQ& Dvr::getDvrMQ() {
+    return *mDvrMQ;
+}
+
 /////////////// FilterCallback ///////////////////////
 //TODO: implement filter callback
 Return<void> FilterCallback::onFilterEvent(const DemuxFilterEvent& /*filterEvent*/) {
@@ -636,6 +653,26 @@
     return NULL;
 }
 
+static int android_media_tv_Tuner_gat_av_sync_hw_id(JNIEnv*, jobject, jobject) {
+    return 0;
+}
+
+static jlong android_media_tv_Tuner_gat_av_sync_time(JNIEnv*, jobject, jint) {
+    return 0;
+}
+
+static int android_media_tv_Tuner_connect_cicam(JNIEnv*, jobject, jint) {
+    return 0;
+}
+
+static int android_media_tv_Tuner_disconnect_cicam(JNIEnv*, jobject) {
+    return 0;
+}
+
+static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv*, jobject, jint) {
+    return NULL;
+}
+
 static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->getLnbIds();
@@ -843,6 +880,10 @@
     return tuner->openDvr(static_cast<DvrType>(type), bufferSize);
 }
 
+static jobject android_media_tv_Tuner_get_demux_caps(JNIEnv*, jobject) {
+    return NULL;
+}
+
 static int android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
     sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
     sp<IFilter> filterSp = getFilter(env, filter)->getIFilter();
@@ -864,12 +905,28 @@
 }
 
 static int android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) {
-    sp<IDvr> dvrSp = getDvr(env, dvr)->getIDvr();
+    sp<Dvr> dvrSp = getDvr(env, dvr);
+    sp<IDvr> iDvrSp = dvrSp->getIDvr();
     if (dvrSp == NULL) {
         ALOGD("Failed to configure dvr: dvr not found");
         return (int)Result::INVALID_STATE;
     }
-    Result result = dvrSp->configure(getDvrSettings(env, settings));
+    Result result = iDvrSp->configure(getDvrSettings(env, settings));
+    MQDescriptorSync<uint8_t> dvrMQDesc;
+    if (result == Result::SUCCESS) {
+        Result getQueueDescResult = Result::UNKNOWN_ERROR;
+        iDvrSp->getQueueDesc(
+                [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
+                    dvrMQDesc = desc;
+                    getQueueDescResult = r;
+                    ALOGD("getDvrQueueDesc");
+                });
+        if (getQueueDescResult == Result::SUCCESS) {
+            dvrSp->mDvrMQ = std::make_unique<DvrMQ>(dvrMQDesc, true);
+            EventFlag::createEventFlag(
+                    dvrSp->mDvrMQ->getEventFlagWord(), &(dvrSp->mDvrMQEventFlag));
+        }
+    }
     return (int)result;
 }
 
@@ -927,6 +984,115 @@
     return 0;
 }
 
+static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jobject jfd) {
+    sp<Dvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to set FD for dvr: dvr not found");
+    }
+    dvrSp->mFd = jniGetFDFromFileDescriptor(env, jfd);
+    ALOGD("set fd = %d", dvrSp->mFd);
+}
+
+static int android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jint size) {
+    sp<Dvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to read dvr: dvr not found");
+    }
+
+    int available = dvrSp->mDvrMQ->availableToWrite();
+    int write = std::min(size, available);
+
+    DvrMQ::MemTransaction tx;
+    int ret = 0;
+    if (dvrSp->mDvrMQ->beginWrite(write, &tx)) {
+        auto first = tx.getFirstRegion();
+        auto data = first.getAddress();
+        int length = first.getLength();
+        int firstToWrite = std::min(length, write);
+        ret = read(dvrSp->mFd, data, firstToWrite);
+        if (ret < firstToWrite) {
+            ALOGW("[DVR] file to MQ, first region: %d bytes to write, but %d bytes written",
+                    firstToWrite, ret);
+        } else if (firstToWrite < write) {
+            ALOGD("[DVR] write second region: %d bytes written, %d bytes in total", ret, write);
+            auto second = tx.getSecondRegion();
+            data = second.getAddress();
+            length = second.getLength();
+            int secondToWrite = std::min(length, write - firstToWrite);
+            ret += read(dvrSp->mFd, data, secondToWrite);
+        }
+        ALOGD("[DVR] file to MQ: %d bytes need to be written, %d bytes written", write, ret);
+        if (!dvrSp->mDvrMQ->commitWrite(ret)) {
+            ALOGE("[DVR] Error: failed to commit write!");
+        }
+
+    } else {
+        ALOGE("dvrMq.beginWrite failed");
+    }
+    return ret;
+}
+
+static int android_media_tv_Tuner_read_dvr_from_array(
+        JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
+        jint /* size */) {
+    //TODO: impl
+    return 0;
+}
+
+static int android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jint size) {
+    sp<Dvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGW("Failed to write dvr: dvr not found");
+        return 0;
+    }
+
+    if (dvrSp->mDvrMQ == NULL) {
+        ALOGW("Failed to write dvr: dvr not configured");
+        return 0;
+    }
+
+    DvrMQ& dvrMq = dvrSp->getDvrMQ();
+
+    int available = dvrMq.availableToRead();
+    int toRead = std::min(size, available);
+
+    int ret = 0;
+    DvrMQ::MemTransaction tx;
+    if (dvrMq.beginRead(toRead, &tx)) {
+        auto first = tx.getFirstRegion();
+        auto data = first.getAddress();
+        int length = first.getLength();
+        int firstToRead = std::min(length, toRead);
+        ret = write(dvrSp->mFd, data, firstToRead);
+        if (ret < firstToRead) {
+            ALOGW("[DVR] MQ to file: %d bytes read, but %d bytes written", firstToRead, ret);
+        } else if (firstToRead < toRead) {
+            ALOGD("[DVR] read second region: %d bytes read, %d bytes in total", ret, toRead);
+            auto second = tx.getSecondRegion();
+            data = second.getAddress();
+            length = second.getLength();
+            int secondToRead = toRead - firstToRead;
+            ret += write(dvrSp->mFd, data, secondToRead);
+        }
+        ALOGD("[DVR] MQ to file: %d bytes to be read, %d bytes written", toRead, ret);
+        if (!dvrMq.commitRead(ret)) {
+            ALOGE("[DVR] Error: failed to commit read!");
+        }
+
+    } else {
+        ALOGE("dvrMq.beginRead failed");
+    }
+
+    return ret;
+}
+
+static int android_media_tv_Tuner_write_dvr_to_array(
+        JNIEnv /* *env */, jobject /* dvr */, jbyteArray /* bytes */, jint /* offset */,
+        jint /* size */) {
+    //TODO: impl
+    return 0;
+}
+
 static const JNINativeMethod gTunerMethods[] = {
     { "nativeInit", "()V", (void *)android_media_tv_Tuner_native_init },
     { "nativeSetup", "()V", (void *)android_media_tv_Tuner_native_setup },
@@ -944,6 +1110,13 @@
     { "nativeSetLna", "(Z)I", (void *)android_media_tv_Tuner_set_lna },
     { "nativeGetFrontendStatus", "([I)[Landroid/media/tv/tuner/FrontendStatus;",
             (void *)android_media_tv_Tuner_get_frontend_status },
+    { "nativeGetAvSyncHwId", "(Landroid/media/tv/tuner/Tuner$Filter;)I",
+            (void *)android_media_tv_Tuner_gat_av_sync_hw_id },
+    { "nativeGetAvSyncTime", "(I)J", (void *)android_media_tv_Tuner_gat_av_sync_time },
+    { "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam },
+    { "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam },
+    { "nativeGetFrontendInfo", "(I)[Landroid/media/tv/tuner/FrontendInfo;",
+            (void *)android_media_tv_Tuner_get_frontend_info },
     { "nativeOpenFilter", "(III)Landroid/media/tv/tuner/Tuner$Filter;",
             (void *)android_media_tv_Tuner_open_filter },
     { "nativeGetLnbIds", "()Ljava/util/List;",
@@ -954,6 +1127,8 @@
             (void *)android_media_tv_Tuner_open_descrambler },
     { "nativeOpenDvr", "(II)Landroid/media/tv/tuner/Tuner$Dvr;",
             (void *)android_media_tv_Tuner_open_dvr },
+    { "nativeGetDemuxCapabilities", "()Landroid/media/tv/tuner/DemuxCapabilities;",
+            (void *)android_media_tv_Tuner_get_demux_caps },
 };
 
 static const JNINativeMethod gFilterMethods[] = {
@@ -966,7 +1141,7 @@
     { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter },
     { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
     { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_filter_fmq },
-    { "nativeCloseFilter", "()I", (void *)android_media_tv_Tuner_close_filter },
+    { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
 };
 
 static const JNINativeMethod gDescramblerMethods[] = {
@@ -975,7 +1150,7 @@
     { "nativeRemovePid", "(IILandroid/media/tv/tuner/Tuner$Filter;)I",
             (void *)android_media_tv_Tuner_remove_pid },
     { "nativeSetKeyToken", "([B)I", (void *)android_media_tv_Tuner_set_key_token },
-    { "nativeClose", "()V", (void *)android_media_tv_Tuner_close_descrambler },
+    { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_descrambler },
 };
 
 static const JNINativeMethod gDvrMethods[] = {
@@ -989,6 +1164,12 @@
     { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
     { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
     { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
+    { "nativeSetFileDescriptor", "(Ljava/io/FileDescriptor;)V",
+            (void *)android_media_tv_Tuner_dvr_set_fd },
+    { "nativeRead", "(I)I", (void *)android_media_tv_Tuner_read_dvr },
+    { "nativeRead", "([BII)I", (void *)android_media_tv_Tuner_read_dvr_from_array },
+    { "nativeWrite", "(I)I", (void *)android_media_tv_Tuner_write_dvr },
+    { "nativeWrite", "([BII)I", (void *)android_media_tv_Tuner_write_dvr_to_array },
 };
 
 static const JNINativeMethod gLnbMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index d37a2d9..5c012bb 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -19,6 +19,8 @@
 
 #include <android/hardware/tv/tuner/1.0/ITuner.h>
 #include <fmq/MessageQueue.h>
+#include <fstream>
+#include <string>
 #include <unordered_map>
 #include <utils/RefBase.h>
 
@@ -58,6 +60,7 @@
 using ::android::hardware::tv::tuner::V1_0::RecordStatus;
 
 using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using DvrMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
 
 namespace android {
 
@@ -80,9 +83,16 @@
 
 struct Dvr : public RefBase {
     Dvr(sp<IDvr> sp, jweak obj);
+    ~Dvr();
+    int close();
+    DvrMQ& getDvrMQ();
     sp<IDvr> getIDvr();
     sp<IDvr> mDvrSp;
     jweak mDvrObj;
+    std::unique_ptr<DvrMQ> mDvrMQ;
+    EventFlag* mDvrMQEventFlag;
+    std::string mFilePath;
+    int mFd;
 };
 
 struct FilterCallback : public IFilterCallback {
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 747d4c01..007dd10 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -268,8 +268,9 @@
 
 static jint
 android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
-        jstring type, jstring uuid, jint priority, jint sessionId, jintArray jId,
-        jobjectArray javadesc, jstring opPackageName)
+        jstring type, jstring uuid, jint priority, jint sessionId,
+        jint deviceType, jstring deviceAddress,
+        jintArray jId, jobjectArray javadesc, jstring opPackageName)
 {
     ALOGV("android_media_AudioEffect_native_setup");
     AudioEffectJniStorage* lpJniStorage = NULL;
@@ -280,6 +281,7 @@
     const char *uuidStr = NULL;
     effect_descriptor_t desc;
     jobject jdesc;
+    AudioDeviceTypeAddr device;
 
     ScopedUtfChars opPackageNameStr(env, opPackageName);
 
@@ -328,6 +330,12 @@
         goto setup_failure;
     }
 
+    if (deviceType != AUDIO_DEVICE_NONE) {
+        device.mType = deviceType;
+        ScopedUtfChars address(env, deviceAddress);
+        device.mAddress = address.c_str();
+    }
+
     // create the native AudioEffect object
     lpAudioEffect = new AudioEffect(typeStr,
                                     String16(opPackageNameStr.c_str()),
@@ -336,7 +344,8 @@
                                     effectCallback,
                                     &lpJniStorage->mCallbackData,
                                     (audio_session_t) sessionId,
-                                    AUDIO_IO_HANDLE_NONE);
+                                    AUDIO_IO_HANDLE_NONE,
+                                    device);
     if (lpAudioEffect == 0) {
         ALOGE("Error creating AudioEffect");
         goto setup_failure;
@@ -757,7 +766,7 @@
 // Dalvik VM type signatures
 static const JNINativeMethod gMethods[] = {
     {"native_init",          "()V",      (void *)android_media_AudioEffect_native_init},
-    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;II[I[Ljava/lang/Object;Ljava/lang/String;)I",
+    {"native_setup",         "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;IIILjava/lang/String;[I[Ljava/lang/Object;Ljava/lang/String;)I",
                                          (void *)android_media_AudioEffect_native_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioEffect_native_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioEffect_native_release},
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
index 10c17dc..6fe847b 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2Test.java
@@ -258,6 +258,7 @@
 
         final CountDownLatch successLatch = new CountDownLatch(1);
         final CountDownLatch failureLatch = new CountDownLatch(1);
+        final List<RouteSessionController> controllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
@@ -266,6 +267,7 @@
                 assertNotNull(controller);
                 assertTrue(createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1));
                 assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                controllers.add(controller);
                 successLatch.countDown();
             }
 
@@ -288,7 +290,7 @@
             // onSessionCreationFailed should not be called.
             assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            // TODO: Release controllers
+            releaseControllers(controllers);
             mRouter2.unregisterRouteCallback(routeCallback);
             mRouter2.unregisterSessionCallback(sessionCallback);
         }
@@ -305,11 +307,13 @@
 
         final CountDownLatch successLatch = new CountDownLatch(1);
         final CountDownLatch failureLatch = new CountDownLatch(1);
+        final List<RouteSessionController> controllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
             public void onSessionCreated(RouteSessionController controller) {
+                controllers.add(controller);
                 successLatch.countDown();
             }
 
@@ -334,7 +338,7 @@
             // onSessionCreated should not be called.
             assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            // TODO: Release controllers
+            releaseControllers(controllers);
             mRouter2.unregisterRouteCallback(routeCallback);
             mRouter2.unregisterSessionCallback(sessionCallback);
         }
@@ -347,7 +351,6 @@
 
         final CountDownLatch successLatch = new CountDownLatch(2);
         final CountDownLatch failureLatch = new CountDownLatch(1);
-
         final List<RouteSessionController> createdControllers = new ArrayList<>();
 
         // Create session with this route
@@ -395,7 +398,7 @@
             assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller1.getControlCategory()));
             assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller2.getControlCategory()));
         } finally {
-            // TODO: Release controllers
+            releaseControllers(createdControllers);
             mRouter2.unregisterRouteCallback(routeCallback);
             mRouter2.unregisterSessionCallback(sessionCallback);
         }
@@ -412,11 +415,13 @@
 
         final CountDownLatch successLatch = new CountDownLatch(1);
         final CountDownLatch failureLatch = new CountDownLatch(1);
+        final List<RouteSessionController> controllers = new ArrayList<>();
 
         // Create session with this route
         SessionCallback sessionCallback = new SessionCallback() {
             @Override
             public void onSessionCreated(RouteSessionController controller) {
+                controllers.add(controller);
                 successLatch.countDown();
             }
 
@@ -442,7 +447,7 @@
             assertFalse(successLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
             assertFalse(failureLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            // TODO: Release controllers
+            releaseControllers(controllers);
             mRouter2.unregisterRouteCallback(routeCallback);
             mRouter2.unregisterSessionCallback(sessionCallback);
         }
@@ -541,8 +546,7 @@
             assertTrue(onSessionInfoChangedLatchForDeselect.await(
                     TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            // TODO: Release controllers
-            controllers.clear();
+            releaseControllers(controllers);
             mRouter2.unregisterRouteCallback(routeCallback);
             mRouter2.unregisterSessionCallback(sessionCallback);
         }
@@ -618,12 +622,79 @@
             controller.transferToRoute(routeToTransferTo);
             assertTrue(onSessionInfoChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         } finally {
-            // TODO: Release controllers
-            controllers.clear();
+            releaseControllers(controllers);
             mRouter2.unregisterRouteCallback(routeCallback);
             mRouter2.unregisterSessionCallback(sessionCallback);
         }
+    }
 
+    // TODO: Add tests for onSessionReleased() call.
+
+    @Test
+    public void testRouteSessionControllerReleaseShouldIgnoreTransferTo() throws Exception {
+        final List<String> sampleControlCategory = new ArrayList<>();
+        sampleControlCategory.add(CATEGORY_SAMPLE);
+
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(sampleControlCategory);
+        MediaRoute2Info routeToCreateSessionWith = routes.get(ROUTE_ID1);
+        assertNotNull(routeToCreateSessionWith);
+
+        final CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+        final CountDownLatch onSessionInfoChangedLatch = new CountDownLatch(1);
+        final List<RouteSessionController> controllers = new ArrayList<>();
+
+        // Create session with ROUTE_ID1
+        SessionCallback sessionCallback = new SessionCallback() {
+            @Override
+            public void onSessionCreated(RouteSessionController controller) {
+                assertNotNull(controller);
+                assertTrue(getRouteIds(controller.getSelectedRoutes()).contains(ROUTE_ID1));
+                assertTrue(TextUtils.equals(CATEGORY_SAMPLE, controller.getControlCategory()));
+                controllers.add(controller);
+                onSessionCreatedLatch.countDown();
+            }
+
+            @Override
+            public void onSessionInfoChanged(RouteSessionController controller,
+                    RouteSessionInfo oldInfo, RouteSessionInfo newInfo) {
+                if (onSessionCreatedLatch.getCount() != 0
+                        || controllers.get(0).getSessionId() != controller.getSessionId()) {
+                    return;
+                }
+                onSessionInfoChangedLatch.countDown();
+            }
+        };
+
+        // TODO: Remove this once the MediaRouter2 becomes always connected to the service.
+        RouteCallback routeCallback = new RouteCallback();
+        mRouter2.registerRouteCallback(mExecutor, routeCallback);
+
+        try {
+            mRouter2.registerSessionCallback(mExecutor, sessionCallback);
+            mRouter2.requestCreateSession(routeToCreateSessionWith, CATEGORY_SAMPLE);
+            assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+            assertEquals(1, controllers.size());
+            RouteSessionController controller = controllers.get(0);
+            assertTrue(getRouteIds(controller.getTransferrableRoutes())
+                    .contains(ROUTE_ID5_TO_TRANSFER_TO));
+
+            // Release controller. Future calls should be ignored.
+            controller.release();
+
+            // Transfer to ROUTE_ID5_TO_TRANSFER_TO
+            MediaRoute2Info routeToTransferTo = routes.get(ROUTE_ID5_TO_TRANSFER_TO);
+            assertNotNull(routeToTransferTo);
+
+            // This call should be ignored.
+            // The onSessionInfoChanged() shouldn't be called.
+            controller.transferToRoute(routeToTransferTo);
+            assertFalse(onSessionInfoChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } finally {
+            releaseControllers(controllers);
+            mRouter2.unregisterRouteCallback(routeCallback);
+            mRouter2.unregisterSessionCallback(sessionCallback);
+        }
     }
 
     // Helper for getting routes easily
@@ -663,6 +734,12 @@
         }
     }
 
+    static void releaseControllers(@NonNull List<RouteSessionController> controllers) {
+        for (RouteSessionController controller : controllers) {
+            controller.release();
+        }
+    }
+
     /**
      * Returns a list of IDs (not uniqueId) of the given route list.
      */
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 1d51c96..83c7c17 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -29,6 +29,7 @@
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2;
 import android.media.MediaRouter2.RouteCallback;
+import android.media.MediaRouter2.SessionCallback;
 import android.media.MediaRouter2Manager;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -101,8 +102,8 @@
     private String mPackageName;
 
     private final List<MediaRouter2Manager.Callback> mManagerCallbacks = new ArrayList<>();
-    private final List<RouteCallback> mRouteCallbacks =
-            new ArrayList<>();
+    private final List<RouteCallback> mRouteCallbacks = new ArrayList<>();
+    private final List<SessionCallback> mSessionCallbacks = new ArrayList<>();
 
     public static final List<String> CATEGORIES_ALL = new ArrayList();
     public static final List<String> CATEGORIES_SPECIAL = new ArrayList();
@@ -205,27 +206,26 @@
     }
 
     /**
-     * Tests if MR2.Callback.onRouteSelected is called when a route is selected from MR2Manager.
-     *
-     * TODO: Change this test so that this test check whether the route is added in a session.
-     *       Until then, temporailiy marking @Ignore
+     * Tests if MR2.SessionCallback.onSessionCreated is called
+     * when a route is selected from MR2Manager.
      */
     @Test
-    @Ignore
-    public void testRouterOnRouteSelected() throws Exception {
+    public void testRouterOnSessionCreated() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CATEGORIES_ALL);
 
         CountDownLatch latch = new CountDownLatch(1);
 
         addManagerCallback(new MediaRouter2Manager.Callback());
-//        addRouterCallback(new RouteDiscoveryCallback() {
-//            @Override
-//            public void onRouteSelected(MediaRoute2Info route, int reason, Bundle controlHints) {
-//                if (route != null && TextUtils.equals(route.getId(), ROUTE_ID1)) {
-//                    latch.countDown();
-//                }
-//            }
-//        });
+        //TODO: remove this when it's not necessary.
+        addRouterCallback(new MediaRouter2.RouteCallback());
+        addSessionCallback(new SessionCallback() {
+            @Override
+            public void onSessionCreated(MediaRouter2.RouteSessionController controller) {
+                if (createRouteMap(controller.getSelectedRoutes()).containsKey(ROUTE_ID1)) {
+                    latch.countDown();
+                }
+            }
+        });
 
         MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
         assertNotNull(routeToSelect);
@@ -446,6 +446,11 @@
         mRouter2.registerRouteCallback(mExecutor, routeCallback);
     }
 
+    private void addSessionCallback(SessionCallback sessionCallback) {
+        mSessionCallbacks.add(sessionCallback);
+        mRouter2.registerSessionCallback(mExecutor, sessionCallback);
+    }
+
     private void clearCallbacks() {
         for (MediaRouter2Manager.Callback callback : mManagerCallbacks) {
             mManager.unregisterCallback(callback);
@@ -456,5 +461,10 @@
             mRouter2.unregisterRouteCallback(routeCallback);
         }
         mRouteCallbacks.clear();
+
+        for (SessionCallback sessionCallback : mSessionCallbacks) {
+            mRouter2.unregisterSessionCallback(sessionCallback);
+        }
+        mSessionCallbacks.clear();
     }
 }
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index 4bcf046..6554267 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -97,22 +97,4 @@
             // Ignore it
         }
     }
-
-    /**
-     * Get carrier-dependent configuration values.
-     *
-     * @param subId the subscription id
-     * @return bundle key/values pairs of configuration values
-     */
-    public Bundle getCarrierConfigValues(int subId) {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.getCarrierConfigValues(subId);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return null;
-    }
 }
diff --git a/mms/java/com/android/internal/telephony/IMms.aidl b/mms/java/com/android/internal/telephony/IMms.aidl
index fa5073e..8be5111 100644
--- a/mms/java/com/android/internal/telephony/IMms.aidl
+++ b/mms/java/com/android/internal/telephony/IMms.aidl
@@ -60,13 +60,6 @@
             in PendingIntent downloadedIntent);
 
     /**
-     * Get carrier-dependent configuration values.
-     *
-     * @param subId the SIM id
-     */
-    Bundle getCarrierConfigValues(int subId);
-
-    /**
      * Import a text message into system's SMS store
      *
      * @param callingPkg the calling app's package name
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
index 033f1b1..bb1336f 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/CryptoSettings.java
@@ -16,7 +16,6 @@
 
 package com.android.server.backup.encryption;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
 
 import android.content.Context;
@@ -29,6 +28,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.security.KeyStoreException;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
@@ -88,8 +88,8 @@
     }
 
     private CryptoSettings(SharedPreferences sharedPreferences, Context context) {
-        mSharedPreferences = checkNotNull(sharedPreferences);
-        mContext = checkNotNull(context);
+        mSharedPreferences = Objects.requireNonNull(sharedPreferences);
+        mContext = Objects.requireNonNull(context);
     }
 
     /**
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
index 3d3fb55..4010bfd 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupFileBuilder.java
@@ -17,7 +17,6 @@
 package com.android.server.backup.encryption.chunking;
 
 import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
 
 import android.annotation.Nullable;
@@ -35,6 +34,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Writes batches of {@link EncryptedChunk} to a diff script, and generates the associated {@link
@@ -174,7 +174,7 @@
      * IllegalStateException}.
      */
     public void finish(ChunksMetadataProto.ChunksMetadata metadata) throws IOException {
-        checkNotNull(metadata, "Metadata cannot be null");
+        Objects.requireNonNull(metadata, "Metadata cannot be null");
 
         long startOfMetadata = mBackupWriter.getBytesWritten();
         mBackupWriter.writeBytes(ChunksMetadataProto.ChunksMetadata.toByteArray(metadata));
@@ -190,7 +190,7 @@
      */
     private void writeChunkToFileAndListing(
             ChunkHash chunkHash, Map<ChunkHash, EncryptedChunk> newChunks) throws IOException {
-        checkNotNull(chunkHash, "Hash cannot be null");
+        Objects.requireNonNull(chunkHash, "Hash cannot be null");
 
         if (mOldChunkListing.hasChunk(chunkHash)) {
             ChunkListingMap.Entry oldChunk = mOldChunkListing.getChunkEntry(chunkHash);
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
index 3ba5f2b..b0a562c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ProtoStore.java
@@ -16,8 +16,6 @@
 
 package com.android.server.backup.encryption.chunking;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AtomicFile;
@@ -34,6 +32,7 @@
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -75,7 +74,7 @@
      */
     @VisibleForTesting
     ProtoStore(Class<T> clazz, File storeFolder) throws IOException {
-        mClazz = checkNotNull(clazz);
+        mClazz = Objects.requireNonNull(clazz);
         mStoreFolder = ensureDirectoryExistsOrThrow(storeFolder);
     }
 
@@ -118,7 +117,7 @@
 
     /** Saves a proto to disk, associating it with the given package. */
     public void saveProto(String packageName, T proto) throws IOException {
-        checkNotNull(proto);
+        Objects.requireNonNull(proto);
         File file = getFileForPackage(packageName);
 
         try (FileOutputStream os = new FileOutputStream(file)) {
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
index 6f4f549..c7af8c8 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
@@ -16,10 +16,9 @@
 
 package com.android.server.backup.encryption.chunking.cdc;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
@@ -49,9 +48,9 @@
      * @throws InvalidKeyException If the salt can not be used as a valid key.
      */
     static byte[] hkdf(byte[] masterKey, byte[] salt, byte[] data) throws InvalidKeyException {
-        checkNotNull(masterKey, "HKDF requires master key to be set.");
-        checkNotNull(salt, "HKDF requires a salt.");
-        checkNotNull(data, "No data provided to HKDF.");
+        Objects.requireNonNull(masterKey, "HKDF requires master key to be set.");
+        Objects.requireNonNull(salt, "HKDF requires a salt.");
+        Objects.requireNonNull(data, "No data provided to HKDF.");
         return hkdfSha256Expand(hkdfSha256Extract(masterKey, salt), data);
     }
 
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
index f356b4f..436c6de8 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
@@ -16,14 +16,14 @@
 
 package com.android.server.backup.encryption.keys;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.IntDef;
 import android.content.Context;
 import android.security.keystore.recovery.InternalRecoveryServiceException;
 import android.security.keystore.recovery.RecoveryController;
 import android.util.Slog;
 
+import java.util.Objects;
+
 import javax.crypto.SecretKey;
 
 /**
@@ -46,8 +46,8 @@
      * @param secretKey The key.
      */
     public RecoverableKeyStoreSecondaryKey(String alias, SecretKey secretKey) {
-        mAlias = checkNotNull(alias);
-        mSecretKey = checkNotNull(secretKey);
+        mAlias = Objects.requireNonNull(alias);
+        mSecretKey = Objects.requireNonNull(secretKey);
     }
 
     /**
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
index b3518e1..217304c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
@@ -17,7 +17,6 @@
 package com.android.server.backup.encryption.kv;
 
 import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
 
 import com.android.server.backup.encryption.chunk.ChunkHash;
 import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
@@ -26,6 +25,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 
 /**
  * Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
@@ -37,7 +37,7 @@
     /** Adds a new pair entry to the listing. */
     public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
         checkArgument(key.length() != 0, "Key must have non-zero length");
-        checkNotNull(hash, "Hash must not be null");
+        Objects.requireNonNull(hash, "Hash must not be null");
 
         KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
         entry.key = key;
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
index 0baec8b..71588f6 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupDataProcessor.java
@@ -16,7 +16,6 @@
 
 package com.android.server.backup.encryption.tasks;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
 
 import android.annotation.Nullable;
@@ -34,6 +33,7 @@
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.security.SecureRandom;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -67,12 +67,12 @@
             SecureRandom secureRandom,
             RecoverableKeyStoreSecondaryKey secondaryKey,
             String packageName) {
-        mContext = checkNotNull(context);
-        mExecutorService = checkNotNull(executorService);
-        mCryptoBackupServer = checkNotNull(cryptoBackupServer);
-        mSecureRandom = checkNotNull(secureRandom);
-        mSecondaryKey = checkNotNull(secondaryKey);
-        mPackageName = checkNotNull(packageName);
+        mContext = Objects.requireNonNull(context);
+        mExecutorService = Objects.requireNonNull(executorService);
+        mCryptoBackupServer = Objects.requireNonNull(cryptoBackupServer);
+        mSecureRandom = Objects.requireNonNull(secureRandom);
+        mSecondaryKey = Objects.requireNonNull(secondaryKey);
+        mPackageName = Objects.requireNonNull(packageName);
     }
 
     @Override
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
index d58cb66..e5e2c1c 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/RotateSecondaryKeyTask.java
@@ -18,8 +18,6 @@
 
 import static android.os.Build.VERSION_CODES.P;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.content.Context;
 import android.security.keystore.recovery.InternalRecoveryServiceException;
 import android.security.keystore.recovery.RecoveryController;
@@ -42,6 +40,7 @@
 import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 
 import javax.crypto.IllegalBlockSizeException;
@@ -77,10 +76,10 @@
             CryptoSettings cryptoSettings,
             RecoveryController recoveryController) {
         mContext = context;
-        mSecondaryKeyManager = checkNotNull(secondaryKeyManager);
-        mCryptoSettings = checkNotNull(cryptoSettings);
-        mBackupServer = checkNotNull(backupServer);
-        mRecoveryController = checkNotNull(recoveryController);
+        mSecondaryKeyManager = Objects.requireNonNull(secondaryKeyManager);
+        mCryptoSettings = Objects.requireNonNull(cryptoSettings);
+        mBackupServer = Objects.requireNonNull(backupServer);
+        mRecoveryController = Objects.requireNonNull(recoveryController);
     }
 
     /** Runs the task. */
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
index 77cfded..81169e2 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/StartSecondaryKeyRotationTask.java
@@ -26,6 +26,7 @@
 import com.android.server.backup.encryption.keys.RecoverableKeyStoreSecondaryKeyManager;
 
 import java.security.UnrecoverableKeyException;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -41,8 +42,8 @@
     public StartSecondaryKeyRotationTask(
             CryptoSettings cryptoSettings,
             RecoverableKeyStoreSecondaryKeyManager secondaryKeyManager) {
-        mCryptoSettings = Preconditions.checkNotNull(cryptoSettings);
-        mSecondaryKeyManager = Preconditions.checkNotNull(secondaryKeyManager);
+        mCryptoSettings = Objects.requireNonNull(cryptoSettings);
+        mSecondaryKeyManager = Objects.requireNonNull(secondaryKeyManager);
     }
 
     /** Begin the key rotation */
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
index faddb6c..7e97924 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/testing/DiffScriptProcessor.java
@@ -17,7 +17,6 @@
 package com.android.server.backup.encryption.testing;
 
 import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
@@ -29,6 +28,7 @@
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Scanner;
 import java.util.regex.Pattern;
@@ -69,7 +69,7 @@
         checkArgument(input.exists(), "input file did not exist.");
         mInput = input;
         mInputLength = input.length();
-        mOutput = checkNotNull(output);
+        mOutput = Objects.requireNonNull(output);
     }
 
     public void process(InputStream diffScript) throws IOException, MalformedDiffScriptException {
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
index 6d3b5e9..06f4859 100644
--- a/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/DataEntity.java
@@ -16,10 +16,9 @@
 
 package com.android.server.testing.shadows;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Represents a key value pair in {@link ShadowBackupDataInput} and {@link ShadowBackupDataOutput}.
@@ -34,7 +33,7 @@
      * StandardCharsets#UTF_8}.
      */
     public DataEntity(String key, String value) {
-        this.mKey = checkNotNull(key);
+        this.mKey = Objects.requireNonNull(key);
         this.mValue = value.getBytes(StandardCharsets.UTF_8);
         mSize = this.mValue.length;
     }
@@ -44,7 +43,7 @@
      * pair.
      */
     public DataEntity(String key) {
-        this.mKey = checkNotNull(key);
+        this.mKey = Objects.requireNonNull(key);
         mSize = -1;
         mValue = null;
     }
@@ -62,7 +61,7 @@
      * @param size the length of the value in bytes
      */
     public DataEntity(String key, byte[] data, int size) {
-        this.mKey = checkNotNull(key);
+        this.mKey = Objects.requireNonNull(key);
         this.mSize = size;
         mValue = new byte[size];
         for (int i = 0; i < size; i++) {
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
deleted file mode 100644
index dd22545..0000000
--- a/packages/CarSystemUI/res/drawable/car_ic_notification_selected.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:viewportWidth="44"
-        android:viewportHeight="44"
-        android:width="44dp"
-        android:height="44dp">
-    <path
-        android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
-        android:fillColor="@color/car_nav_icon_fill_color_selected" />
-</vector>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_selected_unseen.xml b/packages/CarSystemUI/res/drawable/car_ic_notification_selected_unseen.xml
deleted file mode 100644
index c5d7728..0000000
--- a/packages/CarSystemUI/res/drawable/car_ic_notification_selected_unseen.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:viewportWidth="44"
-        android:viewportHeight="44"
-        android:width="44dp"
-        android:height="44dp">
-  <path
-      android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
-      android:fillColor="@color/car_nav_icon_fill_color_selected" />
-  <group
-      android:translateX="30"
-      android:translateY="2">
-    <path
-        android:fillColor="@color/car_nav_notification_unseen_indicator_color"
-        android:strokeWidth="1"
-        android:pathData="M 6 0 C 9.31370849898 0 12 2.68629150102 12 6 C 12 9.31370849898 9.31370849898 12 6 12 C 2.68629150102 12 0 9.31370849898 0 6 C 0 2.68629150102 2.68629150102 0 6 0 Z" />
-  </group>
-</vector>
-
diff --git a/packages/CarSystemUI/res/drawable/car_ic_notification_unseen.xml b/packages/CarSystemUI/res/drawable/car_ic_unseen_indicator.xml
similarity index 69%
rename from packages/CarSystemUI/res/drawable/car_ic_notification_unseen.xml
rename to packages/CarSystemUI/res/drawable/car_ic_unseen_indicator.xml
index 25d1af3..025fc9c 100644
--- a/packages/CarSystemUI/res/drawable/car_ic_notification_unseen.xml
+++ b/packages/CarSystemUI/res/drawable/car_ic_unseen_indicator.xml
@@ -19,14 +19,11 @@
         android:viewportHeight="44"
         android:width="44dp"
         android:height="44dp">
-  <path
-      android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z"
-      android:fillColor="@color/car_nav_icon_fill_color" />
   <group
       android:translateX="30"
       android:translateY="2">
     <path
-        android:fillColor="@color/car_nav_notification_unseen_indicator_color"
+        android:fillColor="@color/car_nav_unseen_indicator_color"
         android:strokeWidth="1"
         android:pathData="M 6 0 C 9.31370849898 0 12 2.68629150102 12 6 C 12 9.31370849898 9.31370849898 12 6 12 C 2.68629150102 12 0 9.31370849898 0 6 C 0 2.68629150102 2.68629150102 0 6 0 Z" />
   </group>
diff --git a/packages/CarSystemUI/res/layout/car_facet_button.xml b/packages/CarSystemUI/res/layout/car_facet_button.xml
deleted file mode 100644
index 8e7ebad..0000000
--- a/packages/CarSystemUI/res/layout/car_facet_button.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/car_facet_button"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:gravity="center"
-        android:animateLayoutChanges="true"
-        android:orientation="vertical">
-
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/car_nav_button_icon"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:animateLayoutChanges="true"
-            android:background="@android:color/transparent"
-            android:scaleType="fitCenter">
-        </com.android.keyguard.AlphaOptimizedImageButton>
-
-        <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/car_nav_button_more_icon"
-            android:layout_height="wrap_content"
-            android:layout_width="match_parent"
-            android:animateLayoutChanges="true"
-            android:src="@drawable/car_ic_arrow"
-            android:background="@android:color/transparent"
-            android:scaleType="fitCenter">
-        </com.android.keyguard.AlphaOptimizedImageButton>
-
-    </LinearLayout>
-</merge>
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
index 6c7a04f..e2e9a33 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml
@@ -33,14 +33,14 @@
         android:paddingEnd="20dp"
         android:gravity="center">
 
-        <com.android.systemui.navigationbar.car.CarFacetButton
+        <com.android.systemui.navigationbar.car.CarNavigationButton
             android:id="@+id/home"
             style="@style/NavigationBarButton"
             systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
             systemui:icon="@drawable/car_ic_overview"
             systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             systemui:selectedIcon="@drawable/car_ic_overview_selected"
-            systemui:useMoreIcon="false"
+            systemui:highlightWhenSelected="true"
         />
 
         <Space
@@ -48,14 +48,14 @@
             android:layout_height="match_parent"
             android:layout_weight="1"/>
 
-        <com.android.systemui.navigationbar.car.CarFacetButton
+        <com.android.systemui.navigationbar.car.CarNavigationButton
             android:id="@+id/maps_nav"
             style="@style/NavigationBarButton"
             systemui:categories="android.intent.category.APP_MAPS"
             systemui:icon="@drawable/car_ic_navigation"
             systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
             systemui:selectedIcon="@drawable/car_ic_navigation_selected"
-            systemui:useMoreIcon="false"
+            systemui:highlightWhenSelected="true"
         />
 
         <Space
@@ -63,7 +63,7 @@
             android:layout_height="match_parent"
             android:layout_weight="1"/>
 
-        <com.android.systemui.navigationbar.car.CarFacetButton
+        <com.android.systemui.navigationbar.car.CarNavigationButton
             android:id="@+id/music_nav"
             style="@style/NavigationBarButton"
             systemui:categories="android.intent.category.APP_MUSIC"
@@ -71,7 +71,7 @@
             systemui:intent="intent:#Intent;action=android.car.intent.action.MEDIA_TEMPLATE;launchFlags=0x10000000;end"
             systemui:packages="com.android.car.media"
             systemui:selectedIcon="@drawable/car_ic_music_selected"
-            systemui:useMoreIcon="false"
+            systemui:highlightWhenSelected="true"
         />
 
         <Space
@@ -79,14 +79,14 @@
             android:layout_height="match_parent"
             android:layout_weight="1"/>
 
-        <com.android.systemui.navigationbar.car.CarFacetButton
+        <com.android.systemui.navigationbar.car.CarNavigationButton
             android:id="@+id/phone_nav"
             style="@style/NavigationBarButton"
             systemui:icon="@drawable/car_ic_phone"
             systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
             systemui:packages="com.android.car.dialer"
             systemui:selectedIcon="@drawable/car_ic_phone_selected"
-            systemui:useMoreIcon="false"
+            systemui:highlightWhenSelected="true"
         />
 
         <Space
@@ -94,14 +94,14 @@
             android:layout_height="match_parent"
             android:layout_weight="1"/>
 
-        <com.android.systemui.navigationbar.car.CarFacetButton
+        <com.android.systemui.navigationbar.car.CarNavigationButton
             android:id="@+id/grid_nav"
             style="@style/NavigationBarButton"
             systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
             systemui:icon="@drawable/car_ic_apps"
             systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
             systemui:selectedIcon="@drawable/car_ic_apps_selected"
-            systemui:useMoreIcon="false"
+            systemui:highlightWhenSelected="true"
         />
 
         <Space
@@ -114,8 +114,6 @@
             style="@style/NavigationBarButton"
             systemui:icon="@drawable/car_ic_notification"
             systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"
-            systemui:selectedIcon="@drawable/car_ic_notification_selected"
-            systemui:useMoreIcon="false"
         />
 
         <Space
@@ -127,7 +125,6 @@
             android:id="@+id/assist"
             style="@style/NavigationBarButton"
             systemui:icon="@drawable/ic_mic_white"
-            systemui:useMoreIcon="false"
         />
 
     </LinearLayout>
@@ -140,8 +137,7 @@
         android:paddingStart="@dimen/car_keyline_1"
         android:paddingEnd="@dimen/car_keyline_1"
         android:gravity="center"
-        android:visibility="gone">
-
-    </LinearLayout>
+        android:visibility="gone"
+    />
 
 </com.android.systemui.navigationbar.car.CarNavigationBarView>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
index 0f964fd..1c5d37f 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -31,7 +31,7 @@
         android:paddingStart="@*android:dimen/car_padding_5"
         android:paddingEnd="@*android:dimen/car_padding_5">
 
-        <com.android.systemui.navigationbar.car.CarFacetButton
+        <com.android.systemui.navigationbar.car.CarNavigationButton
             android:id="@+id/home"
             android:layout_width="@*android:dimen/car_touch_target_size"
             android:layout_height="match_parent"
@@ -39,7 +39,8 @@
             systemui:icon="@drawable/car_ic_overview"
             systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
             systemui:selectedIcon="@drawable/car_ic_overview_selected"
-            systemui:useMoreIcon="false"/>
+            systemui:highlightWhenSelected="true"
+        />
     </LinearLayout>
 </com.android.systemui.navigationbar.car.CarNavigationBarView>
 
diff --git a/packages/CarSystemUI/res/layout/car_navigation_button.xml b/packages/CarSystemUI/res/layout/car_navigation_button.xml
index 6d8cca9..837252b 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -18,12 +18,48 @@
 -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <com.android.keyguard.AlphaOptimizedImageButton
+    <FrameLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/car_nav_button_icon"
-        android:layout_height="wrap_content"
-        android:layout_width="@dimen/car_navigation_button_width"
-        android:layout_centerInParent="true"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
         android:animateLayoutChanges="true"
-        android:scaleType="fitCenter">
-    </com.android.keyguard.AlphaOptimizedImageButton>
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_icon_image"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_gravity="center"
+            android:animateLayoutChanges="true"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter"
+            android:clickable="false"
+        />
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_more_icon"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_gravity="center"
+            android:animateLayoutChanges="true"
+            android:src="@drawable/car_ic_arrow"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter"
+            android:clickable="false"
+        />
+
+        <ImageView
+            android:id="@+id/car_nav_button_unseen_icon"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_gravity="center"
+            android:src="@drawable/car_ic_unseen_indicator"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter"
+            android:clickable="false"
+        />
+
+    </FrameLayout>
 </merge>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 37cd1d4..0b34626 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -93,7 +93,7 @@
         android:layout_height="match_parent"
         android:visibility="invisible"/>
 
-    <ViewStub android:id="@+id/status_bar_expanded"
+    <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="invisible"/>
diff --git a/packages/CarSystemUI/res/values/attrs.xml b/packages/CarSystemUI/res/values/attrs.xml
index 6178738..54026af 100644
--- a/packages/CarSystemUI/res/values/attrs.xml
+++ b/packages/CarSystemUI/res/values/attrs.xml
@@ -16,6 +16,12 @@
   -->
 
 <resources>
+    <attr name="icon" format="reference"/>
+    <attr name="selectedIcon" format="reference"/>
+    <attr name="intent" format="string"/>
+    <attr name="longIntent" format="string"/>
+    <attr name="selectedAlpha" format="float" />
+    <attr name="unselectedAlpha" format="float" />
 
     <!-- Custom attributes to configure hvac values -->
     <declare-styleable name="AnimatedTemperatureView">
@@ -32,4 +38,67 @@
         <attr name="android:minEms"/>
         <attr name="android:textAppearance"/>
     </declare-styleable>
+
+    <!-- Allow for custom attribs to be added to a nav button -->
+    <declare-styleable name="CarNavigationButton">
+        <!-- intent to start when button is click -->
+        <attr name="intent" />
+        <!-- intent to start when a long press has happened -->
+        <attr name="longIntent" />
+        <!-- start the intent as a broad cast instead of an activity if true-->
+        <attr name="broadcast" format="boolean"/>
+        <!-- Alpha value to used when in selected state.  Defaults 1f  -->
+        <attr name="selectedAlpha" />
+        <!-- Alpha value to used when in un-selected state.  Defaults 0.7f  -->
+        <attr name="unselectedAlpha" />
+        <!-- icon to be rendered when in selected state -->
+        <attr name="selectedIcon" />
+        <!-- icon to be rendered (drawable) -->
+        <attr name="icon"/>
+        <!-- categories that will be added as extras to the fired intents -->
+        <attr name="categories" format="string"/>
+        <!-- package names that will be added as extras to the fired intents -->
+        <attr name="packages" format="string" />
+        <!-- componentName names that will be used for detecting selected state -->
+        <attr name="componentNames" format="string" />
+        <!-- whether to highlight the button when selected. Defaults false -->
+        <attr name="showMoreWhenSelected" format="boolean" />
+        <!-- whether to highlight the button when selected. Defaults false -->
+        <attr name="highlightWhenSelected" format="boolean" />
+    </declare-styleable>
+
+    <!-- Custom attributes to configure hvac values -->
+    <declare-styleable name="TemperatureView">
+        <attr name="hvacAreaId" format="integer"/>
+        <attr name="hvacPropertyId" format="integer"/>
+        <attr name="hvacTempFormat" format="string"/>
+    </declare-styleable>
+
+    <declare-styleable name="carVolumeItems"/>
+    <declare-styleable name="carVolumeItems_item">
+        <!-- Align with AudioAttributes.USAGE_* -->
+        <attr name="usage">
+            <enum name="unknown" value="0"/>
+            <enum name="media" value="1"/>
+            <enum name="voice_communication" value="2"/>
+            <enum name="voice_communication_signalling" value="3"/>
+            <enum name="alarm" value="4"/>
+            <enum name="notification" value="5"/>
+            <enum name="notification_ringtone" value="6"/>
+            <enum name="notification_communication_request" value="7"/>
+            <enum name="notification_communication_instant" value="8"/>
+            <enum name="notification_communication_delayed" value="9"/>
+            <enum name="notification_event" value="10"/>
+            <enum name="assistance_accessibility" value="11"/>
+            <enum name="assistance_navigation_guidance" value="12"/>
+            <enum name="assistance_sonification" value="13"/>
+            <enum name="game" value="14"/>
+            <!-- hidden, do not use -->
+            <!-- enum name="virtual_source" value="15"/ -->
+            <enum name="assistant" value="16"/>
+        </attr>
+
+        <!-- Icon resource ids to render on UI -->
+        <attr name="icon" />
+    </declare-styleable>
 </resources>
diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml
index 5fcf38fc..7972e09 100644
--- a/packages/CarSystemUI/res/values/colors.xml
+++ b/packages/CarSystemUI/res/values/colors.xml
@@ -43,8 +43,8 @@
     <!-- The color of the dividing line between grouped notifications. -->
     <color name="notification_divider_color">@*android:color/notification_action_list</color>
 
-    <!-- The color for the unseen notification indicator. -->
-    <color name="car_nav_notification_unseen_indicator_color">#e25142</color>
+    <!-- The color for the unseen indicator. -->
+    <color name="car_nav_unseen_indicator_color">#e25142</color>
 
     <!-- The color of the ripples on the untinted notifications -->
     <color name="notification_ripple_untinted_color">@color/ripple_material_light</color>
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
index c50de22..98cc00e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/AssitantButton.java
@@ -30,9 +30,9 @@
 /**
  * AssitantButton is a ui component that will trigger the Voice Interaction Service.
  */
-public class AssitantButton extends CarFacetButton {
+public class AssitantButton extends CarNavigationButton {
 
-    private static final String TAG = "CarFacetButton";
+    private static final String TAG = "AssistantButton";
     private final AssistUtils mAssistUtils;
     private IVoiceInteractionSessionShowCallback mShowCallback =
             new IVoiceInteractionSessionShowCallback.Stub() {
@@ -62,7 +62,7 @@
     }
 
     @Override
-    protected void setupIntents(TypedArray typedArray) {
+    protected void setUpIntents(TypedArray typedArray) {
         // left blank because for the assistant button Intent will not be passed from the layout.
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java
new file mode 100644
index 0000000..c36aaa0
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateController.java
@@ -0,0 +1,232 @@
+/*
+ * 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.navigationbar.car;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.Display;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * CarNavigationButtons can optionally have selection state that toggles certain visual indications
+ * based on whether the active application on screen is associated with it. This is basically a
+ * similar concept to a radio button group.
+ *
+ * This class controls the selection state of CarNavigationButtons that have opted in to have such
+ * selection state-dependent visual indications.
+ */
+@Singleton
+public class ButtonSelectionStateController {
+
+    private final Set<CarNavigationButton> mRegisteredViews = new HashSet<>();
+
+    protected ButtonMap mButtonsByCategory = new ButtonMap();
+    protected ButtonMap mButtonsByPackage = new ButtonMap();
+    protected ButtonMap mButtonsByComponentName = new ButtonMap();
+    protected HashSet<CarNavigationButton> mSelectedButtons;
+    protected Context mContext;
+
+    @Inject
+    public ButtonSelectionStateController(Context context) {
+        mContext = context;
+        mSelectedButtons = new HashSet<>();
+    }
+
+    /**
+     * Iterate through a view looking for CarNavigationButton and add it to the controller if it
+     * opted in to be highlighted when the active application is associated with it.
+     *
+     * @param v the View that may contain CarFacetButtons
+     */
+    protected void addAllButtonsWithSelectionState(View v) {
+        if (v instanceof CarNavigationButton) {
+            if (((CarNavigationButton) v).hasSelectionState()) {
+                addButtonWithSelectionState((CarNavigationButton) v);
+            }
+        } else if (v instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) v;
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                addAllButtonsWithSelectionState(viewGroup.getChildAt(i));
+            }
+        }
+    }
+
+    /** Removes all buttons from the button maps. */
+    protected void removeAll() {
+        mButtonsByCategory.clear();
+        mButtonsByPackage.clear();
+        mButtonsByComponentName.clear();
+        mSelectedButtons.clear();
+        mRegisteredViews.clear();
+    }
+
+    /**
+     * This will unselect the currently selected CarNavigationButton and determine which one should
+     * be selected next. It does this by reading the properties on the CarNavigationButton and
+     * seeing if they are a match with the supplied StackInfo list.
+     * The order of selection detection is ComponentName, PackageName then Category
+     * They will then be compared with the supplied StackInfo list.
+     * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
+     * for consideration if it has the same displayId as the CarNavigationButton.
+     *
+     * @param stackInfoList of the currently running application
+     * @param validDisplay index of the valid display
+     */
+
+    protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList, int validDisplay) {
+        ActivityManager.StackInfo validStackInfo = null;
+        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
+            // Find the first stack info with a topActivity in the primary display.
+            // TODO: We assume that CarFacetButton will launch an app only in the primary display.
+            // We need to extend the functionality to handle the multiple display properly.
+            if (stackInfo.topActivity != null && stackInfo.displayId == validDisplay) {
+                validStackInfo = stackInfo;
+                break;
+            }
+        }
+
+        if (validStackInfo == null) {
+            // No stack was found that was on the same display as the buttons thus return
+            return;
+        }
+        int displayId = validStackInfo.displayId;
+
+        mSelectedButtons.forEach(carNavigationButton -> {
+            if (carNavigationButton.getDisplayId() == displayId) {
+                carNavigationButton.setSelected(false);
+            }
+        });
+        mSelectedButtons.clear();
+
+        HashSet<CarNavigationButton> selectedButtons = findSelectedButtons(validStackInfo);
+
+        if (selectedButtons != null) {
+            selectedButtons.forEach(carNavigationButton -> {
+                if (carNavigationButton.getDisplayId() == displayId) {
+                    carNavigationButton.setSelected(true);
+                    mSelectedButtons.add(carNavigationButton);
+                }
+            });
+        }
+    }
+
+    /**
+     * Defaults to Display.DEFAULT_DISPLAY when no parameter is provided for the validDisplay.
+     *
+     * @param stackInfoList
+     */
+    protected void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
+        taskChanged(stackInfoList, Display.DEFAULT_DISPLAY);
+    }
+
+    /**
+     * Add navigation button to this controller if it uses selection state.
+     */
+    private void addButtonWithSelectionState(CarNavigationButton carNavigationButton) {
+        if (mRegisteredViews.contains(carNavigationButton)) {
+            return;
+        }
+        String[] categories = carNavigationButton.getCategories();
+        for (int i = 0; i < categories.length; i++) {
+            mButtonsByCategory.add(categories[i], carNavigationButton);
+        }
+
+        String[] packages = carNavigationButton.getPackages();
+        for (int i = 0; i < packages.length; i++) {
+            mButtonsByPackage.add(packages[i], carNavigationButton);
+        }
+        String[] componentNames = carNavigationButton.getComponentName();
+        for (int i = 0; i < componentNames.length; i++) {
+            mButtonsByComponentName.add(componentNames[i], carNavigationButton);
+        }
+
+        mRegisteredViews.add(carNavigationButton);
+    }
+
+    private HashSet<CarNavigationButton> findSelectedButtons(
+            ActivityManager.StackInfo validStackInfo) {
+        String packageName = validStackInfo.topActivity.getPackageName();
+
+        HashSet<CarNavigationButton> selectedButtons =
+                findButtonsByComponentName(validStackInfo.topActivity);
+        if (selectedButtons == null) {
+            selectedButtons = mButtonsByPackage.get(packageName);
+        }
+        if (selectedButtons == null) {
+            String category = getPackageCategory(packageName);
+            if (category != null) {
+                selectedButtons = mButtonsByCategory.get(category);
+            }
+        }
+
+        return selectedButtons;
+    }
+
+    private HashSet<CarNavigationButton> findButtonsByComponentName(
+            ComponentName componentName) {
+        HashSet<CarNavigationButton> buttons =
+                mButtonsByComponentName.get(componentName.flattenToShortString());
+        return (buttons != null) ? buttons :
+                mButtonsByComponentName.get(componentName.flattenToString());
+    }
+
+    private String getPackageCategory(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        Set<String> supportedCategories = mButtonsByCategory.keySet();
+        for (String category : supportedCategories) {
+            Intent intent = new Intent();
+            intent.setPackage(packageName);
+            intent.setAction(Intent.ACTION_MAIN);
+            intent.addCategory(category);
+            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+            if (list.size() > 0) {
+                // Cache this package name into ButtonsByPackage map, so we won't have to query
+                // all categories next time this package name shows up.
+                mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
+                return category;
+            }
+        }
+        return null;
+    }
+
+    // simple multi-map
+    private static class ButtonMap extends HashMap<String, HashSet<CarNavigationButton>> {
+
+        public boolean add(String key, CarNavigationButton value) {
+            if (containsKey(key)) {
+                return get(key).add(value);
+            }
+            HashSet<CarNavigationButton> set = new HashSet<>();
+            set.add(value);
+            put(key, set);
+            return true;
+        }
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java
similarity index 75%
rename from packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java
rename to packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java
index 4925220..9da4121 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/FacetButtonTaskStackListener.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/ButtonSelectionStateListener.java
@@ -24,28 +24,25 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import dagger.Lazy;
-
 /**
  * An implementation of TaskStackChangeListener, that listens for changes in the system
  * task stack and notifies the navigation bar.
  */
 @Singleton
-class FacetButtonTaskStackListener extends TaskStackChangeListener {
-    private static final String TAG = FacetButtonTaskStackListener.class.getSimpleName();
+class ButtonSelectionStateListener extends TaskStackChangeListener {
+    private static final String TAG = ButtonSelectionStateListener.class.getSimpleName();
 
-    private final Lazy<CarFacetButtonController> mFacetButtonControllerLazy;
+    private final ButtonSelectionStateController mButtonSelectionStateController;
 
     @Inject
-    FacetButtonTaskStackListener(
-            Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
-        mFacetButtonControllerLazy = carFacetButtonControllerLazy;
+    ButtonSelectionStateListener(ButtonSelectionStateController carNavigationButtonController) {
+        mButtonSelectionStateController = carNavigationButtonController;
     }
 
     @Override
     public void onTaskStackChanged() {
         try {
-            mFacetButtonControllerLazy.get().taskChanged(
+            mButtonSelectionStateController.taskChanged(
                     ActivityTaskManager.getService().getAllStackInfos());
         } catch (Exception e) {
             Log.e(TAG, "Getting StackInfo from activity manager failed", e);
@@ -55,7 +52,7 @@
     @Override
     public void onTaskDisplayChanged(int taskId, int newDisplayId) {
         try {
-            mFacetButtonControllerLazy.get().taskChanged(
+            mButtonSelectionStateController.taskChanged(
                     ActivityTaskManager.getService().getAllStackInfos());
         } catch (Exception e) {
             Log.e(TAG, "Getting StackInfo from activity manager failed", e);
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
deleted file mode 100644
index 0b89992..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButton.java
+++ /dev/null
@@ -1,226 +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.navigationbar.car;
-
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.os.Build;
-import android.os.UserHandle;
-import android.util.AttributeSet;
-import android.view.Display;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.keyguard.AlphaOptimizedImageButton;
-import com.android.systemui.R;
-
-/**
- * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
- * category. It can also render a indicator implying that there are more options of apps to launch
- * using this component. This is done with a "More icon" currently an arrow as defined in the layout
- * file. The class is to serve as an example.
- *
- * New activity will be launched on the same display as the button is on.
- * Usage example: A button that allows a user to select a music app and indicate that there are
- * other music apps installed.
- */
-public class CarFacetButton extends LinearLayout {
-    private static final String FACET_FILTER_DELIMITER = ";";
-    /**
-     * Extra information to be sent to a helper to make the decision of what app to launch when
-     * clicked.
-     */
-    private static final String EXTRA_FACET_CATEGORIES = "categories";
-    private static final String EXTRA_FACET_PACKAGES = "packages";
-    private static final String EXTRA_FACET_ID = "filter_id";
-    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
-    private static final String TAG = "CarFacetButton";
-
-    private Context mContext;
-    private AlphaOptimizedImageButton mIcon;
-    private AlphaOptimizedImageButton mMoreIcon;
-    private boolean mSelected = false;
-    private String[] mComponentNames;
-    /** App categories that are to be used with this widget */
-    private String[] mFacetCategories;
-    /** App packages that are allowed to be used with this widget */
-    private String[] mFacetPackages;
-    private int mIconResourceId;
-    /**
-     * If defined in the xml this will be the icon that's rendered when the button is marked as
-     * selected
-     */
-    private int mSelectedIconResourceId;
-    private boolean mUseMoreIcon = true;
-    private float mSelectedAlpha = 1f;
-    private float mUnselectedAlpha = 1f;
-
-    public CarFacetButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mContext = context;
-        View.inflate(context, R.layout.car_facet_button, this);
-        // extract custom attributes
-        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
-        setupIntents(typedArray);
-        setupIcons(typedArray);
-    }
-
-    /**
-     * Reads the custom attributes to setup click handlers for this component.
-     */
-    protected void setupIntents(TypedArray typedArray) {
-        String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
-        String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
-        String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
-        String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
-        String componentNameString =
-                typedArray.getString(R.styleable.CarFacetButton_componentNames);
-        try {
-            final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
-            intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
-
-            if (packageString != null) {
-                mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
-                intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
-            }
-            if (categoryString != null) {
-                mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
-                intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
-            }
-            if (componentNameString != null) {
-                mComponentNames = componentNameString.split(FACET_FILTER_DELIMITER);
-            }
-
-            setOnClickListener(v -> {
-                ActivityOptions options = ActivityOptions.makeBasic();
-                options.setLaunchDisplayId(mContext.getDisplayId());
-                intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
-                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-                mContext.sendBroadcastAsUser(
-                        new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
-            });
-
-            if (longPressIntentString != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
-                final Intent longPressIntent = Intent.parseUri(longPressIntentString,
-                        Intent.URI_INTENT_SCHEME);
-                setOnLongClickListener(v -> {
-                    ActivityOptions options = ActivityOptions.makeBasic();
-                    options.setLaunchDisplayId(mContext.getDisplayId());
-                    mContext.startActivityAsUser(longPressIntent, options.toBundle(),
-                            UserHandle.CURRENT);
-                    mContext.sendBroadcastAsUser(
-                            new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.CURRENT);
-                    return true;
-                });
-            }
-        } catch (Exception e) {
-            throw new RuntimeException("Failed to attach intent", e);
-        }
-    }
-
-    private void setupIcons(TypedArray styledAttributes) {
-        mSelectedAlpha = styledAttributes.getFloat(
-                R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
-        mUnselectedAlpha = styledAttributes.getFloat(
-                R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
-        mIcon = findViewById(R.id.car_nav_button_icon);
-        mIcon.setScaleType(ImageView.ScaleType.CENTER);
-        mIcon.setClickable(false);
-        mIcon.setAlpha(mUnselectedAlpha);
-        mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
-        mIcon.setImageResource(mIconResourceId);
-        mSelectedIconResourceId = styledAttributes.getResourceId(
-                R.styleable.CarFacetButton_selectedIcon, mIconResourceId);
-
-        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
-        mMoreIcon.setClickable(false);
-        mMoreIcon.setAlpha(mSelectedAlpha);
-        mMoreIcon.setVisibility(GONE);
-        mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
-    }
-
-    /**
-     * @return The app categories the component represents
-     */
-    public String[] getCategories() {
-        if (mFacetCategories == null) {
-            return new String[0];
-        }
-        return mFacetCategories;
-    }
-
-    /**
-     * @return The valid packages that should be considered.
-     */
-    public String[] getFacetPackages() {
-        if (mFacetPackages == null) {
-            return new String[0];
-        }
-        return mFacetPackages;
-    }
-
-    /**
-     * @return The list of component names.
-     */
-    public String[] getComponentName() {
-        if (mComponentNames == null) {
-            return new String[0];
-        }
-        return mComponentNames;
-    }
-
-    /**
-     * Updates the alpha of the icons to "selected" and shows the "More icon"
-     *
-     * @param selected true if the view must be selected, false otherwise
-     */
-    public void setSelected(boolean selected) {
-        super.setSelected(selected);
-        setSelected(selected, selected);
-    }
-
-    /**
-     * Updates the visual state to let the user know if it's been selected.
-     *
-     * @param selected true if should update the alpha of the icon to selected, false otherwise
-     * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
-     * is ignored if the attribute useMoreIcon is set to false
-     */
-    public void setSelected(boolean selected, boolean showMoreIcon) {
-        mSelected = selected;
-        mIcon.setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
-        mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
-        if (mUseMoreIcon) {
-            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
-        }
-    }
-
-    /**
-     * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
-     * a display.
-     */
-    public int getDisplayId() {
-        Display display = getDisplay();
-        if (display == null) {
-            return Display.INVALID_DISPLAY;
-        }
-        return display.getDisplayId();
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
deleted file mode 100644
index f66e828..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarFacetButtonController.java
+++ /dev/null
@@ -1,215 +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.navigationbar.car;
-
-import android.app.ActivityManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.view.Display;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
- * application on screen is associated with it. This is basically a similar concept to a radio
- * button group.
- */
-@Singleton
-public class CarFacetButtonController {
-
-    private final Set<CarFacetButton> mRegisteredViews = new HashSet<>();
-
-    protected ButtonMap mButtonsByCategory = new ButtonMap();
-    protected ButtonMap mButtonsByPackage = new ButtonMap();
-    protected ButtonMap mButtonsByComponentName = new ButtonMap();
-    protected HashSet<CarFacetButton> mSelectedFacetButtons;
-    protected Context mContext;
-
-    @Inject
-    public CarFacetButtonController(Context context) {
-        mContext = context;
-        mSelectedFacetButtons = new HashSet<>();
-    }
-
-    /**
-     * Add facet button to this controller. The expected use is for the facet button
-     * to get a reference to this controller via {@link com.android.systemui.Dependency}
-     * and self add.
-     */
-    private void addFacetButton(CarFacetButton facetButton) {
-        if (mRegisteredViews.contains(facetButton)) {
-            return;
-        }
-
-        String[] categories = facetButton.getCategories();
-        for (int i = 0; i < categories.length; i++) {
-            mButtonsByCategory.add(categories[i], facetButton);
-        }
-
-        String[] facetPackages = facetButton.getFacetPackages();
-        for (int i = 0; i < facetPackages.length; i++) {
-            mButtonsByPackage.add(facetPackages[i], facetButton);
-        }
-        String[] componentNames = facetButton.getComponentName();
-        for (int i = 0; i < componentNames.length; i++) {
-            mButtonsByComponentName.add(componentNames[i], facetButton);
-        }
-
-        mRegisteredViews.add(facetButton);
-    }
-
-    /** Removes all buttons from the button maps. */
-    public void removeAll() {
-        mButtonsByCategory.clear();
-        mButtonsByPackage.clear();
-        mButtonsByComponentName.clear();
-        mSelectedFacetButtons.clear();
-        mRegisteredViews.clear();
-    }
-
-    /**
-     * Iterate through a view looking for CarFacetButtons and adding them to the controller if found
-     *
-     * @param v the View that may contain CarFacetButtons
-     */
-    public void addAllFacetButtons(View v) {
-        if (v instanceof CarFacetButton) {
-            addFacetButton((CarFacetButton) v);
-        } else if (v instanceof ViewGroup) {
-            ViewGroup viewGroup = (ViewGroup) v;
-            for (int i = 0; i < viewGroup.getChildCount(); i++) {
-                addAllFacetButtons(viewGroup.getChildAt(i));
-            }
-        }
-    }
-
-    /**
-     * This will unselect the currently selected CarFacetButton and determine which one should be
-     * selected next. It does this by reading the properties on the CarFacetButton and seeing if
-     * they are a match with the supplied StackInfo list.
-     * The order of selection detection is ComponentName, PackageName then Category
-     * They will then be compared with the supplied StackInfo list.
-     * The StackInfo is expected to be supplied in order of recency and StackInfo will only be used
-     * for consideration if it has the same displayId as the CarFacetButtons.
-     *
-     * @param stackInfoList of the currently running application
-     */
-    public void taskChanged(List<ActivityManager.StackInfo> stackInfoList) {
-        ActivityManager.StackInfo validStackInfo = null;
-        for (ActivityManager.StackInfo stackInfo : stackInfoList) {
-            // Find the first stack info with a topActivity in the primary display.
-            // TODO: We assume that CarFacetButton will launch an app only in the primary display.
-            // We need to extend the functionality to handle the mutliple display properly.
-            if (stackInfo.topActivity != null && stackInfo.displayId == Display.DEFAULT_DISPLAY) {
-                validStackInfo = stackInfo;
-                break;
-            }
-        }
-
-        if (validStackInfo == null) {
-            // No stack was found that was on the same display as the facet buttons thus return
-            return;
-        }
-
-        if (mSelectedFacetButtons != null) {
-            Iterator<CarFacetButton> iterator = mSelectedFacetButtons.iterator();
-            while (iterator.hasNext()) {
-                CarFacetButton carFacetButton = iterator.next();
-                if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
-                    carFacetButton.setSelected(false);
-                    iterator.remove();
-                }
-            }
-        }
-
-        String packageName = validStackInfo.topActivity.getPackageName();
-        HashSet<CarFacetButton> facetButton =
-                findFacetButtonByComponentName(validStackInfo.topActivity);
-        if (facetButton == null) {
-            facetButton = mButtonsByPackage.get(packageName);
-        }
-
-        if (facetButton == null) {
-            String category = getPackageCategory(packageName);
-            if (category != null) {
-                facetButton = mButtonsByCategory.get(category);
-            }
-        }
-
-        if (facetButton != null) {
-            for (CarFacetButton carFacetButton : facetButton) {
-                if (carFacetButton.getDisplayId() == validStackInfo.displayId) {
-                    carFacetButton.setSelected(true);
-                    mSelectedFacetButtons.add(carFacetButton);
-                }
-            }
-        }
-
-    }
-
-    private HashSet<CarFacetButton> findFacetButtonByComponentName(ComponentName componentName) {
-        HashSet<CarFacetButton> buttons =
-                mButtonsByComponentName.get(componentName.flattenToShortString());
-        return (buttons != null) ? buttons :
-                mButtonsByComponentName.get(componentName.flattenToString());
-    }
-
-    protected String getPackageCategory(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        Set<String> supportedCategories = mButtonsByCategory.keySet();
-        for (String category : supportedCategories) {
-            Intent intent = new Intent();
-            intent.setPackage(packageName);
-            intent.setAction(Intent.ACTION_MAIN);
-            intent.addCategory(category);
-            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
-            if (list.size() > 0) {
-                // Cache this package name into facetPackageMap, so we won't have to query
-                // all categories next time this package name shows up.
-                mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
-                return category;
-            }
-        }
-        return null;
-    }
-
-    // simple multi-map
-    private static class ButtonMap extends HashMap<String, HashSet<CarFacetButton>> {
-
-        public boolean add(String key, CarFacetButton value) {
-            if (containsKey(key)) {
-                return get(key).add(value);
-            }
-            HashSet<CarFacetButton> set = new HashSet<>();
-            set.add(value);
-            put(key, set);
-            return true;
-        }
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
index 59a084e..d8c9d17 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java
@@ -57,12 +57,12 @@
     private final WindowManager mWindowManager;
     private final CarDeviceProvisionedController mCarDeviceProvisionedController;
     private final CommandQueue mCommandQueue;
-    private final Lazy<FacetButtonTaskStackListener> mFacetButtonTaskStackListenerLazy;
+    private final ButtonSelectionStateListener mButtonSelectionStateListener;
     private final Handler mMainHandler;
     private final Lazy<KeyguardStateController> mKeyguardStateControllerLazy;
     private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
     private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
-    private final Lazy<CarFacetButtonController> mCarFacetButtonControllerLazy;
+    private final ButtonSelectionStateController mButtonSelectionStateController;
 
     private IStatusBarService mBarService;
     private ActivityManagerWrapper mActivityManagerWrapper;
@@ -92,24 +92,24 @@
             WindowManager windowManager,
             DeviceProvisionedController deviceProvisionedController,
             CommandQueue commandQueue,
-            Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListenerLazy,
+            ButtonSelectionStateListener buttonSelectionStateListener,
             @Main Handler mainHandler,
             Lazy<KeyguardStateController> keyguardStateControllerLazy,
             Lazy<NavigationBarController> navigationBarControllerLazy,
             SuperStatusBarViewFactory superStatusBarViewFactory,
-            Lazy<CarFacetButtonController> carFacetButtonControllerLazy) {
+            ButtonSelectionStateController buttonSelectionStateController) {
         super(context);
         mCarNavigationBarController = carNavigationBarController;
         mWindowManager = windowManager;
         mCarDeviceProvisionedController = (CarDeviceProvisionedController)
                 deviceProvisionedController;
         mCommandQueue = commandQueue;
-        mFacetButtonTaskStackListenerLazy = facetButtonTaskStackListenerLazy;
+        mButtonSelectionStateListener = buttonSelectionStateListener;
         mMainHandler = mainHandler;
         mKeyguardStateControllerLazy = keyguardStateControllerLazy;
         mNavigationBarControllerLazy = navigationBarControllerLazy;
         mSuperStatusBarViewFactory = superStatusBarViewFactory;
-        mCarFacetButtonControllerLazy = carFacetButtonControllerLazy;
+        mButtonSelectionStateController = buttonSelectionStateController;
     }
 
     @Override
@@ -156,7 +156,7 @@
         createNavigationBar(result);
 
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
-        mActivityManagerWrapper.registerTaskStackListener(mFacetButtonTaskStackListenerLazy.get());
+        mActivityManagerWrapper.registerTaskStackListener(mButtonSelectionStateListener);
 
         mCarNavigationBarController.connectToHvac();
     }
@@ -181,7 +181,7 @@
         // remove and reattach all hvac components such that we don't keep a reference to unused
         // ui elements
         mCarNavigationBarController.removeAllFromHvac();
-        mCarFacetButtonControllerLazy.get().removeAll();
+        mButtonSelectionStateController.removeAll();
 
         if (mTopNavigationBarWindow != null) {
             mTopNavigationBarWindow.removeAllViews();
@@ -343,7 +343,7 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("  mTaskStackListener=");
-        pw.println(mFacetButtonTaskStackListenerLazy.get());
+        pw.println(mButtonSelectionStateListener);
         pw.print("  mBottomNavigationBarView=");
         pw.println(mBottomNavigationBarView);
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
index 6f28843..a56c4ed 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBarController.java
@@ -37,7 +37,7 @@
 
     private final Context mContext;
     private final NavigationBarViewFactory mNavigationBarViewFactory;
-    private final Lazy<CarFacetButtonController> mCarFacetButtonControllerLazy;
+    private final ButtonSelectionStateController mButtonSelectionStateController;
     private final Lazy<HvacController> mHvacControllerLazy;
 
     private boolean mShowBottom;
@@ -58,11 +58,11 @@
     @Inject
     public CarNavigationBarController(Context context,
             NavigationBarViewFactory navigationBarViewFactory,
-            Lazy<CarFacetButtonController> carFacetButtonControllerLazy,
+            ButtonSelectionStateController buttonSelectionStateController,
             Lazy<HvacController> hvacControllerLazy) {
         mContext = context;
         mNavigationBarViewFactory = navigationBarViewFactory;
-        mCarFacetButtonControllerLazy = carFacetButtonControllerLazy;
+        mButtonSelectionStateController = buttonSelectionStateController;
         mHvacControllerLazy = hvacControllerLazy;
 
         // Read configuration.
@@ -175,7 +175,7 @@
             NotificationsShadeController notifShadeController) {
         view.setStatusBarWindowTouchListener(statusBarTouchListener);
         view.setNotificationsPanelController(notifShadeController);
-        mCarFacetButtonControllerLazy.get().addAllFacetButtons(view);
+        mButtonSelectionStateController.addAllButtonsWithSelectionState(view);
         mHvacControllerLazy.get().addTemperatureViewToController(view);
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
index 922bfff..b4d4785 100644
--- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
+++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationButton.java
@@ -24,8 +24,12 @@
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Display;
+import android.view.View;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 
+import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
 
 import java.net.URISyntaxException;
@@ -35,110 +39,64 @@
  * xml file level. This allows for more control via overlays instead of having to update
  * code.
  */
-public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
-    private static final String TAG = "CarNavigationButton";
+public class CarNavigationButton extends LinearLayout {
 
-    private static final int UNSEEN_ICON_RESOURCE_ID = R.drawable.car_ic_notification_unseen;
-    private static final int UNSEEN_SELECTED_ICON_RESOURCE_ID =
-            R.drawable.car_ic_notification_selected_unseen;
+    protected static final float DEFAULT_SELECTED_ALPHA = 1f;
+    protected static final float DEFAULT_UNSELECTED_ALPHA = 0.75f;
+
+    private static final String TAG = "CarNavigationButton";
+    private static final String BUTTON_FILTER_DELIMITER = ";";
+    private static final String EXTRA_BUTTON_CATEGORIES = "categories";
+    private static final String EXTRA_BUTTON_PACKAGES = "packages";
 
     private Context mContext;
+    private AlphaOptimizedImageButton mIcon;
+    private AlphaOptimizedImageButton mMoreIcon;
+    private ImageView mUnseenIcon;
     private String mIntent;
     private String mLongIntent;
     private boolean mBroadcastIntent;
     private boolean mHasUnseen = false;
     private boolean mSelected = false;
-    private float mSelectedAlpha = 1f;
-    private float mUnselectedAlpha = 1f;
+    private float mSelectedAlpha;
+    private float mUnselectedAlpha;
     private int mSelectedIconResourceId;
     private int mIconResourceId;
-
+    private String[] mComponentNames;
+    /** App categories that are to be used with this widget */
+    private String[] mButtonCategories;
+    /** App packages that are allowed to be used with this widget */
+    private String[] mButtonPackages;
+    /** Whether to display more icon beneath the primary icon when the button is selected */
+    private boolean mShowMoreWhenSelected = false;
+    /** Whether to highlight the button if the active application is associated with it */
+    private boolean mHighlightWhenSelected = false;
 
     public CarNavigationButton(Context context, AttributeSet attrs) {
         super(context, attrs);
         mContext = context;
-
+        View.inflate(mContext, R.layout.car_navigation_button, /* root= */ this);
         // CarNavigationButton attrs
-        TypedArray typedArray = context.obtainStyledAttributes(
-                attrs, R.styleable.CarNavigationButton);
-        mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
-        mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
-        mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
-        mSelectedAlpha = typedArray.getFloat(
-                R.styleable.CarNavigationButton_selectedAlpha, mSelectedAlpha);
-        mUnselectedAlpha = typedArray.getFloat(
-                R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha);
-        mSelectedIconResourceId = typedArray.getResourceId(
-                R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
-        mIconResourceId = typedArray.getResourceId(
-                R.styleable.CarNavigationButton_icon, 0);
+        TypedArray typedArray = context.obtainStyledAttributes(attrs,
+                R.styleable.CarNavigationButton);
+
+        setUpIntents(typedArray);
+        setUpIcons(typedArray);
         typedArray.recycle();
     }
 
-
-    /**
-     * After the standard inflate this then adds the xml defined intents to click and long click
-     * actions if defined.
-     */
-    @Override
-    public void onFinishInflate() {
-        super.onFinishInflate();
-        setScaleType(ImageView.ScaleType.CENTER);
-        setAlpha(mUnselectedAlpha);
-        setImageResource(mIconResourceId);
-        try {
-            if (mIntent != null) {
-                final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
-                setOnClickListener(v -> {
-                    try {
-                        if (mBroadcastIntent) {
-                            mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT);
-                            mContext.sendBroadcastAsUser(
-                                    new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
-                                    UserHandle.CURRENT);
-                            return;
-                        }
-                        ActivityOptions options = ActivityOptions.makeBasic();
-                        options.setLaunchDisplayId(mContext.getDisplayId());
-                        mContext.startActivityAsUser(intent, options.toBundle(),
-                                UserHandle.CURRENT);
-                    } catch (Exception e) {
-                        Log.e(TAG, "Failed to launch intent", e);
-                    }
-                });
-            }
-        } catch (URISyntaxException e) {
-            throw new RuntimeException("Failed to attach intent", e);
-        }
-
-        try {
-            if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
-                final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
-                setOnLongClickListener(v -> {
-                    try {
-                        ActivityOptions options = ActivityOptions.makeBasic();
-                        options.setLaunchDisplayId(mContext.getDisplayId());
-                        mContext.startActivityAsUser(intent, options.toBundle(),
-                                UserHandle.CURRENT);
-                    } catch (Exception e) {
-                        Log.e(TAG, "Failed to launch intent", e);
-                    }
-                    // consume event either way
-                    return true;
-                });
-            }
-        } catch (URISyntaxException e) {
-            throw new RuntimeException("Failed to attach long press intent", e);
-        }
-    }
-
     /**
      * @param selected true if should indicate if this is a selected state, false otherwise
      */
     public void setSelected(boolean selected) {
         super.setSelected(selected);
         mSelected = selected;
-        setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+        if (mHighlightWhenSelected) {
+            setAlpha(mSelected ? mSelectedAlpha : mUnselectedAlpha);
+        }
+        if (mShowMoreWhenSelected && mMoreIcon != null) {
+            mMoreIcon.setVisibility(selected ? VISIBLE : GONE);
+        }
         updateImage();
     }
 
@@ -155,12 +113,172 @@
         return mHasUnseen;
     }
 
-    private void updateImage() {
-        if (mHasUnseen) {
-            setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
-                    : UNSEEN_ICON_RESOURCE_ID);
-        } else {
-            setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+    /**
+     * @return The app categories the component represents
+     */
+    public String[] getCategories() {
+        if (mButtonCategories == null) {
+            return new String[0];
+        }
+        return mButtonCategories;
+    }
+
+    /**
+     * @return The valid packages that should be considered.
+     */
+    public String[] getPackages() {
+        if (mButtonPackages == null) {
+            return new String[0];
+        }
+        return mButtonPackages;
+    }
+
+    /**
+     * @return The list of component names.
+     */
+    public String[] getComponentName() {
+        if (mComponentNames == null) {
+            return new String[0];
+        }
+        return mComponentNames;
+    }
+
+    /**
+     * @return The id of the display the button is on or Display.INVALID_DISPLAY if it's not yet on
+     * a display.
+     */
+    protected int getDisplayId() {
+        Display display = getDisplay();
+        if (display == null) {
+            return Display.INVALID_DISPLAY;
+        }
+        return display.getDisplayId();
+    }
+
+    protected boolean hasSelectionState() {
+        return mHighlightWhenSelected || mShowMoreWhenSelected;
+    }
+
+    /**
+     * Sets up intents for click, long touch, and broadcast.
+     */
+    protected void setUpIntents(TypedArray typedArray) {
+        mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
+        mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
+        mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
+
+        String categoryString = typedArray.getString(R.styleable.CarNavigationButton_categories);
+        String packageString = typedArray.getString(R.styleable.CarNavigationButton_packages);
+        String componentNameString =
+                typedArray.getString(R.styleable.CarNavigationButton_componentNames);
+
+        try {
+            if (mIntent != null) {
+                final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
+                setOnClickListener(getButtonClickListener(intent));
+                if (packageString != null) {
+                    mButtonPackages = packageString.split(BUTTON_FILTER_DELIMITER);
+                    intent.putExtra(EXTRA_BUTTON_PACKAGES, mButtonPackages);
+                }
+                if (categoryString != null) {
+                    mButtonCategories = categoryString.split(BUTTON_FILTER_DELIMITER);
+                    intent.putExtra(EXTRA_BUTTON_CATEGORIES, mButtonCategories);
+                }
+                if (componentNameString != null) {
+                    mComponentNames = componentNameString.split(BUTTON_FILTER_DELIMITER);
+                }
+            }
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to attach intent", e);
+        }
+
+        try {
+            if (mLongIntent != null && (Build.IS_ENG || Build.IS_USERDEBUG)) {
+                final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
+                setOnLongClickListener(getButtonLongClickListener(intent));
+            }
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to attach long press intent", e);
         }
     }
+
+    /** Defines the behavior of a button click. */
+    protected OnClickListener getButtonClickListener(Intent toSend) {
+        return v -> {
+            mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+                    UserHandle.CURRENT);
+            try {
+                if (mBroadcastIntent) {
+                    mContext.sendBroadcastAsUser(toSend, UserHandle.CURRENT);
+                    return;
+                }
+                ActivityOptions options = ActivityOptions.makeBasic();
+                options.setLaunchDisplayId(mContext.getDisplayId());
+                mContext.startActivityAsUser(toSend, options.toBundle(),
+                        UserHandle.CURRENT);
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to launch intent", e);
+            }
+        };
+    }
+
+    /** Defines the behavior of a long click. */
+    protected OnLongClickListener getButtonLongClickListener(Intent toSend) {
+        return v -> {
+            mContext.sendBroadcastAsUser(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+                    UserHandle.CURRENT);
+            try {
+                ActivityOptions options = ActivityOptions.makeBasic();
+                options.setLaunchDisplayId(mContext.getDisplayId());
+                mContext.startActivityAsUser(toSend, options.toBundle(),
+                        UserHandle.CURRENT);
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to launch intent", e);
+            }
+            // consume event either way
+            return true;
+        };
+    }
+
+
+    /**
+     * Initializes view-related aspects of the button.
+     */
+    private void setUpIcons(TypedArray typedArray) {
+        mSelectedAlpha = typedArray.getFloat(
+                R.styleable.CarNavigationButton_selectedAlpha, DEFAULT_SELECTED_ALPHA);
+        mUnselectedAlpha = typedArray.getFloat(
+                R.styleable.CarNavigationButton_unselectedAlpha, DEFAULT_UNSELECTED_ALPHA);
+        mHighlightWhenSelected = typedArray.getBoolean(
+                R.styleable.CarNavigationButton_highlightWhenSelected,
+                mHighlightWhenSelected);
+        mShowMoreWhenSelected = typedArray.getBoolean(
+                R.styleable.CarNavigationButton_showMoreWhenSelected,
+                mShowMoreWhenSelected);
+
+        mSelectedIconResourceId = typedArray.getResourceId(
+                R.styleable.CarNavigationButton_selectedIcon, mIconResourceId);
+        mIconResourceId = typedArray.getResourceId(
+                R.styleable.CarNavigationButton_icon, 0);
+
+        mIcon = findViewById(R.id.car_nav_button_icon_image);
+        mIcon.setScaleType(ImageView.ScaleType.CENTER);
+        // Always apply selected alpha if the button does not toggle alpha based on selection state.
+        mIcon.setAlpha(mHighlightWhenSelected ? mUnselectedAlpha : mSelectedAlpha);
+        mIcon.setImageResource(mIconResourceId);
+
+        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
+        mMoreIcon.setAlpha(mSelectedAlpha);
+        mMoreIcon.setVisibility(GONE);
+
+        mUnseenIcon = findViewById(R.id.car_nav_button_unseen_icon);
+
+        mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
+    }
+
+    private void updateImage() {
+        mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+        mUnseenIcon.setVisibility(mHasUnseen ? VISIBLE : GONE);
+    }
+
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
index d79849c..4e0fd4a 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarBatteryController.java
@@ -252,6 +252,11 @@
     }
 
     @Override
+    public boolean isPluggedIn() {
+        return true;
+    }
+
+    @Override
     public boolean isPowerSave() {
         // Power save is not valid for the car, so always return false.
         return false;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 77db54c..3c3ebe2 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -128,11 +128,11 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -465,7 +465,7 @@
 
         super.start();
 
-        mNotificationPanel.setScrollingEnabled(true);
+        mNotificationPanelViewController.setScrollingEnabled(true);
         mSettleOpenPercentage = mContext.getResources().getInteger(
                 R.integer.notification_settle_open_percentage);
         mSettleClosePercentage = mContext.getResources().getInteger(
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 1ebaef7..a1eccce 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -86,11 +86,11 @@
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
diff --git a/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml b/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
new file mode 100644
index 0000000..f0e0216
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/car_button_selection_state_controller_test.xml
@@ -0,0 +1,55 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@id/nav_buttons"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:paddingStart="20dp"
+    android:paddingEnd="20dp"
+    android:gravity="center">
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/detectable_by_component_name"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        systemui:highlightWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/detectable_by_category"
+        style="@style/NavigationBarButton"
+        systemui:categories="android.intent.category.APP_MAPS"
+        systemui:icon="@drawable/car_ic_navigation"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MAPS;launchFlags=0x14000000;end"
+        systemui:highlightWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/detectable_by_package"
+        style="@style/NavigationBarButton"
+        systemui:icon="@drawable/car_ic_phone"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end"
+        systemui:packages="com.android.car.dialer"
+        systemui:highlightWhenSelected="true"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml b/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
new file mode 100644
index 0000000..576928c
--- /dev/null
+++ b/packages/CarSystemUI/tests/res/layout/car_navigation_button_test.xml
@@ -0,0 +1,115 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@id/nav_buttons"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_weight="1"
+    android:paddingStart="20dp"
+    android:paddingEnd="20dp"
+    android:gravity="center">
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/default_no_selection_state"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        systemui:selectedIcon="@drawable/car_ic_overview_selected"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/app_grid_activity"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+        systemui:icon="@drawable/car_ic_apps"
+        systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+        systemui:selectedIcon="@drawable/car_ic_apps_selected"
+        systemui:highlightWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/long_click_app_grid_activity"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
+        systemui:icon="@drawable/car_ic_apps"
+        systemui:longIntent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
+        systemui:selectedIcon="@drawable/car_ic_apps_selected"
+        systemui:highlightWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/broadcast"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@null"
+        systemui:broadcast="true"
+        systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/selected_icon_undefined"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/highlightable_no_more_button"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        systemui:selectedIcon="@drawable/car_ic_overview_selected"
+        systemui:highlightWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/not_highlightable_more_button"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        systemui:selectedIcon="@drawable/car_ic_overview_selected"
+        systemui:showMoreWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/highlightable_more_button"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        systemui:selectedIcon="@drawable/car_ic_overview_selected"
+        systemui:highlightWhenSelected="true"
+        systemui:showMoreWhenSelected="true"
+    />
+
+    <com.android.systemui.navigationbar.car.CarNavigationButton
+        android:id="@+id/broadcast"
+        style="@style/NavigationBarButton"
+        systemui:componentNames="com.android.car.carlauncher/.CarLauncher"
+        systemui:icon="@drawable/car_ic_overview"
+        systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"
+        systemui:selectedIcon="@drawable/car_ic_overview_selected"
+        systemui:broadcast="true"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java
new file mode 100644
index 0000000..f94dd82
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/ButtonSelectionStateControllerTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.navigationbar.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class ButtonSelectionStateControllerTest extends SysuiTestCase {
+
+    private static final String TEST_COMPONENT_NAME_PACKAGE = "com.android.car.carlauncher";
+    private static final String TEST_COMPONENT_NAME_CLASS = ".CarLauncher";
+    private static final String TEST_CATEGORY = "com.google.android.apps.maps";
+    private static final String TEST_CATEGORY_CLASS = ".APP_MAPS";
+    private static final String TEST_PACKAGE = "com.android.car.dialer";
+    private static final String TEST_PACKAGE_CLASS = ".Dialer";
+
+    // LinearLayout with CarNavigationButtons with different configurations.
+    private LinearLayout mTestView;
+    private ButtonSelectionStateController mButtonSelectionStateController;
+    private ComponentName mComponentName;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.car_button_selection_state_controller_test, /* root= */ null);
+        mButtonSelectionStateController = new ButtonSelectionStateController(mContext);
+        mButtonSelectionStateController.addAllButtonsWithSelectionState(mTestView);
+    }
+
+    @Test
+    public void onTaskChanged_buttonDetectableByComponentName_selectsAssociatedButton() {
+        CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_component_name);
+        mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
+        List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+        testButton.setSelected(false);
+        mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
+
+        assertbuttonSelected(testButton);
+    }
+
+    @Test
+    public void onTaskChanged_buttonDetectableByCategory_selectsAssociatedButton() {
+        CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_category);
+        mComponentName = new ComponentName(TEST_CATEGORY, TEST_CATEGORY_CLASS);
+        List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+        testButton.setSelected(false);
+        mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
+
+        assertbuttonSelected(testButton);
+    }
+
+    @Test
+    public void onTaskChanged_buttonDetectableByPackage_selectsAssociatedButton() {
+        CarNavigationButton testButton = mTestView.findViewById(R.id.detectable_by_package);
+        mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
+        List<ActivityManager.StackInfo> testStack = createTestStack(mComponentName);
+        testButton.setSelected(false);
+        mButtonSelectionStateController.taskChanged(testStack, /* validDisplay= */ -1);
+
+        assertbuttonSelected(testButton);
+    }
+
+    @Test
+    public void onTaskChanged_deselectsPreviouslySelectedButton() {
+        CarNavigationButton oldButton = mTestView.findViewById(R.id.detectable_by_component_name);
+        mComponentName = new ComponentName(TEST_COMPONENT_NAME_PACKAGE, TEST_COMPONENT_NAME_CLASS);
+        List<ActivityManager.StackInfo> oldStack = createTestStack(mComponentName);
+        oldButton.setSelected(false);
+        mButtonSelectionStateController.taskChanged(oldStack, /* validDisplay= */ -1);
+
+        mComponentName = new ComponentName(TEST_PACKAGE, TEST_PACKAGE_CLASS);
+        List<ActivityManager.StackInfo> newStack = createTestStack(mComponentName);
+        mButtonSelectionStateController.taskChanged(newStack, /* validDisplay= */ -1);
+
+        assertButtonUnselected(oldButton);
+    }
+
+    // Comparing alpha is a valid way to verify button selection state because all test buttons use
+    // highlightWhenSelected = true.
+    private void assertbuttonSelected(CarNavigationButton button) {
+        assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+    }
+
+    private void assertButtonUnselected(CarNavigationButton button) {
+        assertThat(button.getAlpha()).isEqualTo(CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
+    }
+
+    private List<ActivityManager.StackInfo> createTestStack(ComponentName componentName) {
+        ActivityManager.StackInfo validStackInfo = new ActivityManager.StackInfo();
+        validStackInfo.displayId = -1; // No display is assigned to this test view
+        validStackInfo.topActivity = componentName;
+
+        List<ActivityManager.StackInfo> testStack = new ArrayList<>();
+        testStack.add(validStackInfo);
+
+        return testStack;
+    }
+}
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
index 642b114..e0c13ed 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationBarControllerTest.java
@@ -51,7 +51,7 @@
     private TestableResources mTestableResources;
 
     @Mock
-    private CarFacetButtonController mCarFacetButtonController;
+    private ButtonSelectionStateController mButtonSelectionStateController;
     @Mock
     private HvacController mHvacController;
 
@@ -69,7 +69,7 @@
     @Test
     public void testConnectToHvac_callsConnect() {
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         mCarNavigationBar.connectToHvac();
 
@@ -79,7 +79,7 @@
     @Test
     public void testRemoveAllFromHvac_callsRemoveAll() {
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         mCarNavigationBar.removeAllFromHvac();
 
@@ -90,7 +90,7 @@
     public void testGetBottomWindow_bottomDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, false);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
 
@@ -101,7 +101,7 @@
     public void testGetBottomWindow_bottomEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
 
@@ -112,7 +112,7 @@
     public void testGetBottomWindow_bottomEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window1 = mCarNavigationBar.getBottomWindow();
         ViewGroup window2 = mCarNavigationBar.getBottomWindow();
@@ -124,7 +124,7 @@
     public void testGetLeftWindow_leftDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, false);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         ViewGroup window = mCarNavigationBar.getLeftWindow();
         assertThat(window).isNull();
     }
@@ -133,7 +133,7 @@
     public void testGetLeftWindow_leftEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getLeftWindow();
 
@@ -144,7 +144,7 @@
     public void testGetLeftWindow_leftEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window1 = mCarNavigationBar.getLeftWindow();
         ViewGroup window2 = mCarNavigationBar.getLeftWindow();
@@ -156,7 +156,7 @@
     public void testGetRightWindow_rightDisabled_returnsNull() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, false);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
 
@@ -167,7 +167,7 @@
     public void testGetRightWindow_rightEnabled_returnsWindow() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
 
@@ -178,7 +178,7 @@
     public void testGetRightWindow_rightEnabled_calledTwice_returnsSameWindow() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window1 = mCarNavigationBar.getRightWindow();
         ViewGroup window2 = mCarNavigationBar.getRightWindow();
@@ -190,7 +190,7 @@
     public void testSetBottomWindowVisibility_setTrue_isVisible() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
         mCarNavigationBar.setBottomWindowVisibility(View.VISIBLE);
@@ -202,7 +202,7 @@
     public void testSetBottomWindowVisibility_setFalse_isGone() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getBottomWindow();
         mCarNavigationBar.setBottomWindowVisibility(View.GONE);
@@ -214,7 +214,7 @@
     public void testSetLeftWindowVisibility_setTrue_isVisible() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getLeftWindow();
         mCarNavigationBar.setLeftWindowVisibility(View.VISIBLE);
@@ -226,7 +226,7 @@
     public void testSetLeftWindowVisibility_setFalse_isGone() {
         mTestableResources.addOverride(R.bool.config_enableLeftNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getLeftWindow();
         mCarNavigationBar.setLeftWindowVisibility(View.GONE);
@@ -238,7 +238,7 @@
     public void testSetRightWindowVisibility_setTrue_isVisible() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
         mCarNavigationBar.setRightWindowVisibility(View.VISIBLE);
@@ -250,7 +250,7 @@
     public void testSetRightWindowVisibility_setFalse_isGone() {
         mTestableResources.addOverride(R.bool.config_enableRightNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         ViewGroup window = mCarNavigationBar.getRightWindow();
         mCarNavigationBar.setRightWindowVisibility(View.GONE);
@@ -262,7 +262,7 @@
     public void testRegisterBottomBarTouchListener_createViewFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View.OnTouchListener controller = bottomBar.getStatusBarWindowTouchListener();
@@ -277,7 +277,7 @@
     public void testRegisterBottomBarTouchListener_registerFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         mCarNavigationBar.registerBottomBarTouchListener(mock(View.OnTouchListener.class));
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
@@ -290,7 +290,7 @@
     public void testRegisterNotificationController_createViewFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         CarNavigationBarController.NotificationsShadeController controller =
@@ -307,7 +307,7 @@
     public void testRegisterNotificationController_registerFirst_registrationSuccessful() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
 
         mCarNavigationBar.registerNotificationController(
                 mock(CarNavigationBarController.NotificationsShadeController.class));
@@ -322,7 +322,7 @@
     public void testShowAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsVisible() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
 
@@ -335,7 +335,7 @@
     public void testShowAllKeyguardButtons_bottomEnabled_bottomNavButtonsGone() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
 
@@ -348,7 +348,7 @@
     public void testHideAllKeyguardButtons_bottomEnabled_bottomKeyguardButtonsGone() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomKeyguardButtons = bottomBar.findViewById(R.id.lock_screen_nav_buttons);
 
@@ -363,7 +363,7 @@
     public void testHideAllKeyguardButtons_bottomEnabled_bottomNavButtonsVisible() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         View bottomButtons = bottomBar.findViewById(R.id.nav_buttons);
 
@@ -378,7 +378,7 @@
     public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_hasUnseen_setCorrectly() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
 
@@ -393,7 +393,7 @@
     public void testToggleAllNotificationsUnseenIndicator_bottomEnabled_noUnseen_setCorrectly() {
         mTestableResources.addOverride(R.bool.config_enableBottomNavigationBar, true);
         mCarNavigationBar = new CarNavigationBarController(mContext, mNavigationBarViewFactory,
-                () -> mCarFacetButtonController, () -> mHvacController);
+                mButtonSelectionStateController, () -> mHvacController);
         CarNavigationBarView bottomBar = mCarNavigationBar.getBottomBar(/* isSetUp= */ true);
         CarNavigationButton notifications = bottomBar.findViewById(R.id.notifications);
 
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
new file mode 100644
index 0000000..96d567d
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.navigationbar.car;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.tests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class CarNavigationButtonTest extends SysuiTestCase {
+
+    private static final String DEFAULT_BUTTON_ACTIVITY_NAME =
+            "com.android.car.carlauncher/.CarLauncher";
+    private static final String APP_GRID_BUTTON_ACTIVITY_NAME =
+            "com.android.car.carlauncher/.AppGridActivity";
+    private static final String BROADCAST_ACTION_NAME =
+            "android.car.intent.action.TOGGLE_HVAC_CONTROLS";
+
+    private ActivityManager mActivityManager;
+    // LinearLayout with CarNavigationButtons with different configurations.
+    private LinearLayout mTestView;
+    // Does not have any selection state which is the default configuration.
+    private CarNavigationButton mDefaultButton;
+
+    @Before
+    public void setUp() {
+        mContext = spy(mContext);
+        mTestView = (LinearLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.car_navigation_button_test, /* root= */ null);
+        mDefaultButton = mTestView.findViewById(R.id.default_no_selection_state);
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+    }
+
+    @Test
+    public void onCreate_iconIsVisible() {
+        AlphaOptimizedImageButton icon = mDefaultButton.findViewById(
+                R.id.car_nav_button_icon_image);
+
+        assertThat(icon.getDrawable()).isNotNull();
+    }
+
+    @Test
+    public void onSelected_selectedIconDefined_togglesIcon() {
+        mDefaultButton.setSelected(true);
+        Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+                R.id.car_nav_button_icon_image)).getDrawable();
+
+
+        mDefaultButton.setSelected(false);
+        Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+                R.id.car_nav_button_icon_image)).getDrawable();
+
+        assertThat(selectedIconDrawable).isNotEqualTo(unselectedIconDrawable);
+    }
+
+    @Test
+    public void onSelected_selectedIconUndefined_displaysSameIcon() {
+        CarNavigationButton selectedIconUndefinedButton = mTestView.findViewById(
+                R.id.selected_icon_undefined);
+
+        selectedIconUndefinedButton.setSelected(true);
+        Drawable selectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+                R.id.car_nav_button_icon_image)).getDrawable();
+
+
+        selectedIconUndefinedButton.setSelected(false);
+        Drawable unselectedIconDrawable = ((AlphaOptimizedImageButton) mDefaultButton.findViewById(
+                R.id.car_nav_button_icon_image)).getDrawable();
+
+        assertThat(selectedIconDrawable).isEqualTo(unselectedIconDrawable);
+    }
+
+    @Test
+    public void onUnselected_doesNotHighlightWhenSelected_applySelectedAlpha() {
+        mDefaultButton.setSelected(false);
+
+        assertThat(mDefaultButton.getAlpha()).isEqualTo(
+                CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+    }
+
+    @Test
+    public void onSelected_doesNotHighlightWhenSelected_applySelectedAlpha() {
+        mDefaultButton.setSelected(true);
+
+        assertThat(mDefaultButton.getAlpha()).isEqualTo(
+                CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+    }
+
+    @Test
+    public void onUnselected_highlightWhenSelected_applyDefaultUnselectedAlpha() {
+        CarNavigationButton highlightWhenSelectedButton = mTestView.findViewById(
+                R.id.highlightable_no_more_button);
+        highlightWhenSelectedButton.setSelected(false);
+
+        assertThat(highlightWhenSelectedButton.getAlpha()).isEqualTo(
+                CarNavigationButton.DEFAULT_UNSELECTED_ALPHA);
+    }
+
+    @Test
+    public void onSelected_highlightWhenSelected_applyDefaultSelectedAlpha() {
+        CarNavigationButton highlightWhenSelectedButton = mTestView.findViewById(
+                R.id.highlightable_no_more_button);
+        highlightWhenSelectedButton.setSelected(true);
+
+        assertThat(highlightWhenSelectedButton.getAlpha()).isEqualTo(
+                CarNavigationButton.DEFAULT_SELECTED_ALPHA);
+    }
+
+    @Test
+    public void onSelected_doesNotShowMoreWhenSelected_doesNotShowMoreIcon() {
+        mDefaultButton.setSelected(true);
+        AlphaOptimizedImageButton moreIcon = mDefaultButton.findViewById(
+                R.id.car_nav_button_more_icon);
+
+        assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onSelected_showMoreWhenSelected_showsMoreIcon() {
+        CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
+                R.id.not_highlightable_more_button);
+        showMoreWhenSelected.setSelected(true);
+        AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+                R.id.car_nav_button_more_icon);
+
+        assertThat(moreIcon.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onUnselected_showMoreWhenSelected_doesNotShowMoreIcon() {
+        CarNavigationButton showMoreWhenSelected = mTestView.findViewById(
+                R.id.highlightable_no_more_button);
+        showMoreWhenSelected.setSelected(true);
+        showMoreWhenSelected.setSelected(false);
+        AlphaOptimizedImageButton moreIcon = showMoreWhenSelected.findViewById(
+                R.id.car_nav_button_more_icon);
+
+        assertThat(moreIcon.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void onClick_launchesIntentActivity() {
+        mDefaultButton.performClick();
+
+        assertThat(getCurrentActivityName()).isEqualTo(DEFAULT_BUTTON_ACTIVITY_NAME);
+
+        CarNavigationButton appGridButton = mTestView.findViewById(R.id.app_grid_activity);
+        appGridButton.performClick();
+
+        assertThat(getCurrentActivityName()).isEqualTo(APP_GRID_BUTTON_ACTIVITY_NAME);
+    }
+
+    @Test
+    public void onLongClick_longIntentDefined_launchesLongIntentActivity() {
+        mDefaultButton.performClick();
+
+        assertThat(getCurrentActivityName()).isEqualTo(DEFAULT_BUTTON_ACTIVITY_NAME);
+
+        CarNavigationButton appGridButton = mTestView.findViewById(
+                R.id.long_click_app_grid_activity);
+        appGridButton.performLongClick();
+
+        assertThat(getCurrentActivityName()).isEqualTo(APP_GRID_BUTTON_ACTIVITY_NAME);
+    }
+
+    @Test
+    public void onClick_useBroadcast_broadcastsIntent() {
+        CarNavigationButton appGridButton = mTestView.findViewById(R.id.broadcast);
+        appGridButton.performClick();
+
+        verify(mContext).sendBroadcastAsUser(argThat(new ArgumentMatcher<Intent>() {
+            @Override
+            public boolean matches(Intent argument) {
+                return argument.getAction().equals(BROADCAST_ACTION_NAME);
+            }
+        }), any());
+    }
+
+    @Test
+    public void onSetUnseen_hasUnseen_showsUnseenIndicator() {
+        mDefaultButton.setUnseen(true);
+        ImageView hasUnseenIndicator = mDefaultButton.findViewById(R.id.car_nav_button_unseen_icon);
+
+        assertThat(hasUnseenIndicator.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    public void onSetUnseen_doesNotHaveUnseen_hidesUnseenIndicator() {
+        mDefaultButton.setUnseen(false);
+        ImageView hasUnseenIndicator = mDefaultButton.findViewById(R.id.car_nav_button_unseen_icon);
+
+        assertThat(hasUnseenIndicator.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    private String getCurrentActivityName() {
+        return mActivityManager.getRunningTasks(1).get(0).topActivity.flattenToShortString();
+    }
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9e49826..c2ce840 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -262,10 +262,11 @@
             return;
         }
 
+        stopForeground(true);
         mJustCancelledByUser = true;
 
         if (mInstallTask.cancel(false)) {
-            // Will cleanup and post status in onResult()
+            // Will stopSelf() in onResult()
             Log.d(TAG, "Cancel request filed successfully");
         } else {
             Log.e(TAG, "Trying to cancel installation while it's already completed.");
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index 8a2948b..3b3933b 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -16,9 +16,6 @@
 
 package com.android.dynsystem;
 
-import static android.os.image.DynamicSystemClient.KEY_SYSTEM_SIZE;
-import static android.os.image.DynamicSystemClient.KEY_USERDATA_SIZE;
-
 import android.app.Activity;
 import android.app.KeyguardManager;
 import android.content.Context;
@@ -88,12 +85,8 @@
     private void startInstallationService() {
         // retrieve data from calling intent
         Intent callingIntent = getIntent();
-
         Uri url = callingIntent.getData();
-        long systemSize = callingIntent.getLongExtra(KEY_SYSTEM_SIZE, 0);
-        long userdataSize = callingIntent.getLongExtra(KEY_USERDATA_SIZE, 0);
-        boolean enableWhenCompleted = callingIntent.getBooleanExtra(
-                DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, false);
+        Bundle extras = callingIntent.getExtras();
 
         sVerifiedUrl = url.toString();
 
@@ -101,10 +94,7 @@
         Intent intent = new Intent(this, DynamicSystemInstallationService.class);
         intent.setData(url);
         intent.setAction(DynamicSystemClient.ACTION_START_INSTALL);
-        intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
-        intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
-        intent.putExtra(
-                DynamicSystemInstallationService.KEY_ENABLE_WHEN_COMPLETED, enableWhenCompleted);
+        intent.putExtras(extras);
 
         Log.d(TAG, "Starting Installation Service");
         startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index 05de3b7..b9d65c5 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -8,7 +8,7 @@
     <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"Αγγλικά (ΗΠΑ), τύπου International"</string>
     <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"Αγγλικά (ΗΠΑ), τύπου Colemak"</string>
     <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"Αγγλικά (ΗΠΑ), τύπου Dvorak"</string>
-    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Αγγλικά (ΗΠΑ), στυλ Workman"</string>
+    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Αγγλικά (ΗΠΑ), στιλ Workman"</string>
     <string name="keyboard_layout_german_label" msgid="8451565865467909999">"Γερμανικά"</string>
     <string name="keyboard_layout_french_label" msgid="813450119589383723">"Γαλλικά"</string>
     <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"Γαλλικά (Καναδά)"</string>
diff --git a/packages/SettingsLib/res/values-ar/arrays.xml b/packages/SettingsLib/res/values-ar/arrays.xml
index 7fc65a3..b4f5253 100644
--- a/packages/SettingsLib/res/values-ar/arrays.xml
+++ b/packages/SettingsLib/res/values-ar/arrays.xml
@@ -170,7 +170,7 @@
   <string-array name="select_logpersist_titles">
     <item msgid="704720725704372366">"إيقاف"</item>
     <item msgid="6014837961827347618">"الكل"</item>
-    <item msgid="7387060437894578132">"الكل دون اللاسلكي"</item>
+    <item msgid="7387060437894578132">"الكل بدون اللاسلكي"</item>
     <item msgid="7300881231043255746">"‏kernel فقط"</item>
   </string-array>
   <string-array name="select_logpersist_summaries">
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index d4e1b63..43d2ece 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -68,14 +68,14 @@
     <string name="bluetooth_connecting" msgid="5871702668260192755">"جارٍ الاتصال…"</string>
     <string name="bluetooth_connected" msgid="8065345572198502293">"الجهاز متصل<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"جارٍ الاقتران..."</string>
-    <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"الجهاز متصل (من دون هاتف)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"الجهاز متصل (من دون وسائط)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"الجهاز متصل (من دون وصول إلى الرسائل)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"الجهاز متصل (من دون هاتف أو وسائط)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"الجهاز متصل (بدون هاتف)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"الجهاز متصل (بدون وسائط)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"الجهاز متصل (بدون وصول إلى الرسائل)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"الجهاز متصل (بدون هاتف أو وسائط)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
     <string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"الجهاز متصل، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"الجهاز متصل (من دون هاتف)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"الجهاز متصل (من دون وسائط)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
-    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"الجهاز متّصل (من دون هاتف أو وسائط)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"الجهاز متصل (بدون هاتف)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"الجهاز متصل (بدون وسائط)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
+    <string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"الجهاز متّصل (بدون هاتف أو وسائط)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level" msgid="3450745316700494425">"نشط، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"مفعّلة، مستوى البطارية: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، المعدّل: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
     <string name="bluetooth_battery_level" msgid="2893696778200201555">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 07d5ccb..5b84b10 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -203,9 +203,9 @@
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"Erabiltzaile honek ezin ditu VPN ezarpenak atzitu"</string>
     <string name="tethering_settings_not_available" msgid="266821736434699780">"Erabiltzaile honek ezin ditu konexioa partekatzeko ezarpenak atzitu"</string>
     <string name="apn_settings_not_available" msgid="1147111671403342300">"Erabiltzaile honek ezin ditu APN ezarpenak atzitu"</string>
-    <string name="enable_adb" msgid="8072776357237289039">"USB arazketa"</string>
+    <string name="enable_adb" msgid="8072776357237289039">"USB bidezko arazketa"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"Gaitu arazketa modua USBa konektatzean"</string>
-    <string name="clear_adb_keys" msgid="3010148733140369917">"Baliogabetu USB arazketarako baimenak"</string>
+    <string name="clear_adb_keys" msgid="3010148733140369917">"Baliogabetu USB bidezko arazketarako baimenak"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Akatsen txostenerako lasterbidea"</string>
     <string name="bugreport_in_power_summary" msgid="1885529649381831775">"Bateriaren menuan, erakutsi akatsen txostena sortzeko botoia"</string>
     <string name="keep_screen_on" msgid="1187161672348797558">"Mantendu aktibo"</string>
@@ -227,8 +227,7 @@
     <string name="tethering_hardware_offload" msgid="4116053719006939161">"Konexioa partekatzeko hardwarearen azelerazioa"</string>
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Erakutsi Bluetooth gailuak izenik gabe"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Desgaitu bolumen absolutua"</string>
-    <!-- no translation found for bluetooth_enable_gabeldorsche (9131730396242883416) -->
-    <skip />
+    <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gaitu Gabeldorsche"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Bluetooth AVRCP bertsioa"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Hautatu Bluetooth AVRCP bertsioa"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Bluetooth bidezko audioaren kodeka"</string>
@@ -268,17 +267,16 @@
     <string name="debug_view_attributes" msgid="3539609843984208216">"Gaitu ikuspegiaren atributuak ikuskatzeko aukera"</string>
     <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Mantendu datu-konexioa beti aktibo, baita wifi-konexioa aktibo dagoenean ere (sare batetik bestera bizkor aldatu ahal izateko)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Erabilgarri badago, erabili konexioa partekatzeko hardwarearen azelerazioa"</string>
-    <string name="adb_warning_title" msgid="7708653449506485728">"USB arazketa onartu?"</string>
-    <string name="adb_warning_message" msgid="8145270656419669221">"USB arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
-    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB arazketarako sarbidea baliogabetu nahi diezu?"</string>
+    <string name="adb_warning_title" msgid="7708653449506485728">"USB bidezko arazketa onartu?"</string>
+    <string name="adb_warning_message" msgid="8145270656419669221">"USB bidezko arazketa garapen-xedeetarako soilik dago diseinatuta. Erabil ezazu ordenagailuaren eta gailuaren artean datuak kopiatzeko, aplikazioak gailuan jakinarazi gabe instalatzeko eta erregistro-datuak irakurtzeko."</string>
+    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Aurretik baimendutako ordenagailu guztiei USB bidezko arazketarako sarbidea baliogabetu nahi diezu?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"Baimendu garapenerako ezarpenak?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Ezarpen hauek garapen-xedeetarako pentsatu dira soilik. Baliteke ezarpenen eraginez gailua matxuratzea edo funtzionamendu okerra izatea."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Egiaztatu USBko aplikazioak"</string>
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Egiaztatu ADB/ADT bidez instalatutako aplikazioak portaera kaltegarriak atzemateko"</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Bluetooth gailuak izenik gabe (MAC helbideak soilik) erakutsiko dira"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Bluetooth bidezko bolumen absolutuaren eginbidea desgaitu egiten du urruneko gailuetan arazoak hautematen badira; esaterako, bolumena ozenegia bada edo ezin bada kontrolatu"</string>
-    <!-- no translation found for bluetooth_enable_gabeldorsche_summary (8472344901097607030) -->
-    <skip />
+    <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorsche eginbide sorta gaitzen du."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Tokiko terminala"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Gaitu tokiko shell-sarbidea duen terminal-aplikazioa"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"HDCP egiaztapena"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 154f386..d0307ec 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -227,8 +227,7 @@
     <string name="tethering_hardware_offload" msgid="4116053719006939161">"हार्डवेयर से तेज़ी लाने के लिए टेदर करें"</string>
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"बिना नाम वाले ब्लूटूथ डिवाइस दिखाएं"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"ब्लूटूथ से आवाज़ के नियंत्रण की सुविधा रोकें"</string>
-    <!-- no translation found for bluetooth_enable_gabeldorsche (9131730396242883416) -->
-    <skip />
+    <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Gabeldorsche चालू करें"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"ब्लूटूथ एवीआरसीपी वर्शन"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"ब्लूटूथ AVRCP वर्शन चुनें"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"ब्लूटूथ ऑडियो कोडेक"</string>
@@ -277,8 +276,7 @@
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"नुकसानदेह व्यवहार के लिए ADB/ADT से इंस्टॉल किए गए ऐप्लिकेशन जाँचें."</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"बिना नाम वाले ब्लूटूथ डिवाइस (केवल MAC पते वाले) दिखाए जाएंगे"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"दूर के डिवाइस पर आवाज़ बहुत बढ़ जाने या उससे नियंत्रण हटने जैसी समस्याएं होने पर, यह ब्लूटूथ के ज़रिए आवाज़ के नियंत्रण की सुविधा रोक देता है."</string>
-    <!-- no translation found for bluetooth_enable_gabeldorsche_summary (8472344901097607030) -->
-    <skip />
+    <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"ब्लूटूथ Gabeldorsche सुविधा का स्टैक चालू करें."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"स्थानीय टर्मिनल"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"लोकल शेल तक पहुंचने की सुविधा देने वाले टर्मिनल ऐप को चालू करें"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"एचडीसीपी जाँच"</string>
diff --git a/packages/SettingsLib/res/values-sl/arrays.xml b/packages/SettingsLib/res/values-sl/arrays.xml
index 4c22bda..d946316 100644
--- a/packages/SettingsLib/res/values-sl/arrays.xml
+++ b/packages/SettingsLib/res/values-sl/arrays.xml
@@ -82,8 +82,6 @@
     <item msgid="1049450003868150455">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
     <item msgid="2908219194098827570">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
     <item msgid="3825367753087348007">"LDAC"</item>
-    <item msgid="5832677994279829983">"Omogoči izbirne kodeke"</item>
-    <item msgid="9205039209798344398">"Onemogoči izbirne kodeke"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_summaries">
     <item msgid="8868109554557331312">"Uporabi sistemsko izbiro (privzeto)"</item>
@@ -92,8 +90,6 @@
     <item msgid="8627333814413492563">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g>"</item>
     <item msgid="3517061573669307965">"Zvok <xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX_HD">aptX™ HD</xliff:g>"</item>
     <item msgid="2553206901068987657">"LDAC"</item>
-    <item msgid="221347164942544028">"Omogoči izbirne kodeke"</item>
-    <item msgid="7416462860415701287">"Onemogoči izbirne kodeke"</item>
   </string-array>
   <string-array name="bluetooth_a2dp_codec_sample_rate_titles">
     <item msgid="926809261293414607">"Uporabi sistemsko izbiro (privzeto)"</item>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 90d7829..efd7c08 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -227,6 +227,7 @@
     <string name="tethering_hardware_offload" msgid="4116053719006939161">"Strojno pospeševanje za internetno povezavo prek mobilnega telefona"</string>
     <string name="bluetooth_show_devices_without_names" msgid="923584526471885819">"Prikaži naprave Bluetooth brez imen"</string>
     <string name="bluetooth_disable_absolute_volume" msgid="1452342324349203434">"Onemogočanje absolutne glasnosti"</string>
+    <string name="bluetooth_enable_gabeldorsche" msgid="9131730396242883416">"Omogoči Gabeldorsche"</string>
     <string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Različica profila AVRCP za Bluetooth"</string>
     <string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Izberite različico profila AVRCP za Bluetooth"</string>
     <string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Zvočni kodek za Bluetooth"</string>
@@ -275,6 +276,7 @@
     <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Preveri, ali so aplikacije, nameščene prek ADB/ADT, škodljive."</string>
     <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Prikazane bodo naprave Bluetooth brez imen (samo z naslovi MAC)"</string>
     <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Onemogoči funkcijo absolutne glasnosti za Bluetooth, če pride do težav z glasnostjo z oddaljenimi napravami, kot je nesprejemljivo visoka glasnost ali pomanjkanje nadzora."</string>
+    <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Omogoči sklad funkcij Bluetooth Gabeldorche."</string>
     <string name="enable_terminal_title" msgid="3834790541986303654">"Lokalni terminal"</string>
     <string name="enable_terminal_summary" msgid="2481074834856064500">"Omogočanje terminalske aplikacije za dostop do lokalne lupine"</string>
     <string name="hdcp_checking_title" msgid="3155692785074095986">"Preverjanje HDCP"</string>
@@ -379,7 +381,7 @@
     <string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalija (rdeča – zelena)"</string>
     <string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalija (modra – rumena)"</string>
     <string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Popravljanje barv"</string>
-    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="5190814747212060815">"To je preskusna funkcija in lahko vpliva na učinkovitost delovanja."</string>
+    <string name="accessibility_display_daltonizer_preference_subtitle" msgid="9137381746633858694">"Popravljanje barv osebam z barvno slepoto pomaga, da vidijo bolj prave barve"</string>
     <string name="daltonizer_type_overridden" msgid="4509604753672535721">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> – <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="8264199158671531431">"Še približno <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 9b4a6d6..7b589370 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -132,6 +132,20 @@
         <item>avrcp16</item>
     </string-array>
 
+    <!-- Titles for Bluetooth MAP Versions -->
+    <string-array name="bluetooth_map_versions">
+        <item>MAP 1.2 (Default)</item>
+        <item>MAP 1.3</item>
+        <item>MAP 1.4</item>
+    </string-array>
+
+    <!-- Values for Bluetooth MAP Versions -->
+    <string-array name="bluetooth_map_version_values">
+        <item>map12</item>
+        <item>map13</item>
+        <item>map14</item>
+    </string-array>
+
     <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50] -->
     <string-array name="bluetooth_a2dp_codec_titles">
         <item>Use System Selection (Default)</item>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f40105d..a7df6db 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -606,6 +606,11 @@
     <!-- UI debug setting: Select Bluetooth AVRCP Version -->
     <string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string>
 
+    <!-- UI debug setting: Select Bluetooth MAP Version [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_select_map_version_string">Bluetooth MAP Version</string>
+    <!-- UI debug setting: Select Bluetooth MAP Version [CHAR LIMIT=NONE] -->
+    <string name="bluetooth_select_map_version_dialog_title">Select Bluetooth MAP Version</string>
+
     <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
     <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
     <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
@@ -699,7 +704,7 @@
     <!-- Summary of checkbox for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume_summary">Disables the Bluetooth absolute volume feature in case of volume issues with remote devices such as unacceptably loud volume or lack of control.</string>
     <!-- Summary of checkbox for enabling Bluetooth Gabeldorche features [CHAR LIMIT=none] -->
-    <string name="bluetooth_enable_gabeldorsche_summary">Enables the Bluetooth Gabeldorche feature stack.</string>
+    <string name="bluetooth_enable_gabeldorsche_summary">Enables the Bluetooth Gabeldorsche feature stack.</string>
 
     <!-- Title of checkbox setting that enables the terminal app. [CHAR LIMIT=32] -->
     <string name="enable_terminal_title">Local terminal</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 58655a2..b4b55f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -203,13 +203,6 @@
         }
     }
 
-    public int getVolume() {
-        if (mService == null) {
-            return 0;
-        }
-        return mService.getVolume();
-    }
-
     public void setVolume(int volume) {
         if (mService == null) {
             return;
@@ -224,20 +217,6 @@
         return mService.getHiSyncId(device);
     }
 
-    public int getDeviceSide(BluetoothDevice device) {
-        if (mService == null) {
-            return BluetoothHearingAid.SIDE_LEFT;
-        }
-        return mService.getDeviceSide(device);
-    }
-
-    public int getDeviceMode(BluetoothDevice device) {
-        if (mService == null) {
-            return BluetoothHearingAid.MODE_MONAURAL;
-        }
-        return mService.getDeviceMode(device);
-    }
-
     public String toString() {
         return NAME;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index e19ac81..6d7e86f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.PowerManager;
+import android.os.UserHandle;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
 import android.text.TextUtils;
@@ -186,7 +187,8 @@
     }
 
     private static void setBatterySaverConfirmationAcknowledged(Context context) {
-        Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
+        Secure.putIntForUser(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1,
+                UserHandle.USER_CURRENT);
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 732e8db..99c7dcf 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -17,9 +17,8 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.widget.Toast;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -31,22 +30,28 @@
 
     private static final String TAG = "InfoMediaDevice";
 
-    private MediaRouter.RouteInfo mRouteInfo;
+    private final MediaRoute2Info mRouteInfo;
+    private final MediaRouter2Manager mRouterManager;
+    private final String mPackageName;
 
-    InfoMediaDevice(Context context, MediaRouter.RouteInfo info) {
+    InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+            String packageName) {
         super(context, MediaDeviceType.TYPE_CAST_DEVICE);
+        mRouterManager = routerManager;
         mRouteInfo = info;
+        mPackageName = packageName;
         initDeviceRecord();
     }
 
     @Override
     public String getName() {
-        return mRouteInfo.getName();
+        return mRouteInfo.getName().toString();
     }
 
     @Override
     public String getSummary() {
-        return null;
+        return mRouteInfo.getClientPackageName() != null
+                ? mContext.getString(R.string.bluetooth_active_no_battery_level) : null;
     }
 
     @Override
@@ -63,15 +68,14 @@
 
     @Override
     public boolean connect() {
-        //TODO(b/121083246): use SystemApi to transfer media
         setConnectedRecord();
-        Toast.makeText(mContext, "This is cast device !", Toast.LENGTH_SHORT).show();
-        return false;
+        mRouterManager.selectRoute(mPackageName, mRouteInfo);
+        return true;
     }
 
     @Override
     public void disconnect() {
-        //TODO(b/121083246): disconnected last select device
+        //TODO(b/144535188): disconnected last select device
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index bc8e2c3..e008cd03 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -17,13 +17,16 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.util.Log;
-
-import androidx.mediarouter.media.MediaRouteSelector;
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
 /**
  * InfoMediaManager provide interface to get InfoMediaDevice list.
  */
@@ -32,62 +35,75 @@
     private static final String TAG = "InfoMediaManager";
 
     @VisibleForTesting
-    final MediaRouterCallback mMediaRouterCallback = new MediaRouterCallback();
+    final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
     @VisibleForTesting
-    MediaRouteSelector mSelector;
+    final Executor mExecutor = Executors.newSingleThreadExecutor();
     @VisibleForTesting
-    MediaRouter mMediaRouter;
+    MediaRouter2Manager mRouterManager;
 
     private String mPackageName;
+    private MediaDevice mCurrentConnectedDevice;
 
-    InfoMediaManager(Context context, String packageName, Notification notification) {
+    public InfoMediaManager(Context context, String packageName, Notification notification) {
         super(context, notification);
 
-        mMediaRouter = MediaRouter.getInstance(context);
-        mPackageName = packageName;
-        mSelector = new MediaRouteSelector.Builder()
-                .addControlCategory(getControlCategoryByPackageName(mPackageName))
-                .build();
+        mRouterManager = MediaRouter2Manager.getInstance(context);
+        if (packageName != null) {
+            mPackageName = packageName;
+        }
     }
 
     @Override
     public void startScan() {
         mMediaDevices.clear();
-        mMediaRouter.addCallback(mSelector, mMediaRouterCallback,
-                MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+        mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
     }
 
     @VisibleForTesting
     String getControlCategoryByPackageName(String packageName) {
         //TODO(b/117129183): Use package name to get ControlCategory.
         //Since api not ready, return fixed ControlCategory for prototype.
-        return "com.google.android.gms.cast.CATEGORY_CAST/4F8B3483";
+        return "com.google.android.gms.cast.CATEGORY_CAST";
     }
 
     @Override
     public void stopScan() {
-        mMediaRouter.removeCallback(mMediaRouterCallback);
+        mRouterManager.unregisterCallback(mMediaRouterCallback);
     }
 
-    class MediaRouterCallback extends MediaRouter.Callback {
-        @Override
-        public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo route) {
-            MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(route));
-            if (mediaDevice == null) {
-                mediaDevice = new InfoMediaDevice(mContext, route);
-                Log.d(TAG, "onRouteAdded() route : " + route.getName());
-                mMediaDevices.add(mediaDevice);
-                dispatchDeviceAdded(mediaDevice);
+    /**
+     * Get current device that played media.
+     * @return MediaDevice
+     */
+    public MediaDevice getCurrentConnectedDevice() {
+        return mCurrentConnectedDevice;
+    }
+
+    class RouterManagerCallback extends MediaRouter2Manager.Callback {
+
+        private void refreshDevices() {
+            mMediaDevices.clear();
+            mCurrentConnectedDevice = null;
+            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);
             }
+            dispatchDeviceListAdded();
         }
 
         @Override
-        public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo route) {
-            final MediaDevice mediaDevice = findMediaDevice(MediaDeviceUtils.getId(route));
-            if (mediaDevice != null) {
-                Log.d(TAG, "onRouteRemoved() route : " + route.getName());
-                mMediaDevices.remove(mediaDevice);
-                dispatchDeviceRemoved(mediaDevice);
+        public void onRoutesAdded(List<MediaRoute2Info> routes) {
+            refreshDevices();
+        }
+
+        @Override
+        public void onControlCategoriesChanged(String packageName, List<String> controlCategories) {
+            if (TextUtils.equals(mPackageName, packageName)) {
+                refreshDevices();
             }
         }
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 4e16c66..5b4ef3a 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;
@@ -59,6 +60,8 @@
     private Context mContext;
     private BluetoothMediaManager mBluetoothMediaManager;
     private LocalBluetoothManager mLocalBluetoothManager;
+    private InfoMediaManager mInfoMediaManager;
+    private String mPackageName;
 
     @VisibleForTesting
     List<MediaDevice> mMediaDevices = new ArrayList<>();
@@ -87,6 +90,7 @@
 
     public LocalMediaManager(Context context, String packageName, Notification notification) {
         mContext = context;
+        mPackageName = packageName;
         mLocalBluetoothManager =
                 LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
         if (mLocalBluetoothManager == null) {
@@ -96,6 +100,7 @@
 
         mBluetoothMediaManager =
                 new BluetoothMediaManager(context, mLocalBluetoothManager, notification);
+        mInfoMediaManager = new InfoMediaManager(context, packageName, notification);
     }
 
     @VisibleForTesting
@@ -104,6 +109,7 @@
         mContext = context;
         mLocalBluetoothManager = localBluetoothManager;
         mBluetoothMediaManager = bluetoothMediaManager;
+        mInfoMediaManager = infoMediaManager;
     }
 
     /**
@@ -126,8 +132,7 @@
             return;
         }
 
-        //TODO(b/121083246): Update it once remote media API is ready.
-        if (mCurrentConnectedDevice != null && !(connectDevice instanceof InfoMediaDevice)) {
+        if (mCurrentConnectedDevice != null) {
             mCurrentConnectedDevice.disconnect();
         }
 
@@ -157,6 +162,10 @@
         mMediaDevices.clear();
         mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
         mBluetoothMediaManager.startScan();
+        if (!TextUtils.isEmpty(mPackageName)) {
+            mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+            mInfoMediaManager.startScan();
+        }
     }
 
     private void addPhoneDeviceIfNecessary() {
@@ -191,6 +200,10 @@
     public void stopScan() {
         mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
         mBluetoothMediaManager.stopScan();
+        if (!TextUtils.isEmpty(mPackageName)) {
+            mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+            mInfoMediaManager.stopScan();
+        }
     }
 
     /**
@@ -253,7 +266,9 @@
                 }
             }
             addPhoneDeviceIfNecessary();
-            mCurrentConnectedDevice = updateCurrentConnectedDevice();
+            final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
+            mCurrentConnectedDevice = infoMediaDevice != null
+                    ? infoMediaDevice : updateCurrentConnectedDevice();
             updatePhoneMediaDeviceSummary();
             dispatchDeviceListUpdate();
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
index 4b8e706..df6929e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDeviceUtils.java
@@ -16,8 +16,7 @@
 package com.android.settingslib.media;
 
 import android.bluetooth.BluetoothDevice;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
@@ -49,12 +48,12 @@
     }
 
     /**
-     * Use RouteInfo id to represent unique id
+     * Use MediaRoute2Info id to represent unique id
      *
-     * @param route the RouteInfo
-     * @return RouteInfo id
+     * @param route the MediaRoute2Info
+     * @return MediaRoute2Info id
      */
-    public static String getId(MediaRouter.RouteInfo route) {
+    public static String getId(MediaRoute2Info route) {
         return route.getId();
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index b13c483..81739e0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1766,7 +1766,10 @@
         if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
             return SECURITY_OWE;
         }
-        return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE;
+        return (config.wepTxKeyIndex >= 0
+                && config.wepTxKeyIndex < config.wepKeys.length
+                && config.wepKeys[config.wepTxKeyIndex] != null)
+                ? SECURITY_WEP : SECURITY_NONE;
     }
 
     public static String securityToString(int security, int pskType) {
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
new file mode 100644
index 0000000..c9db0d1
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.media;
+
+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.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+
+import com.android.settingslib.R;
+
+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;
+
+@RunWith(RobolectricTestRunner.class)
+public class InfoMediaDeviceTest {
+
+    private static final String TEST_PACKAGE_NAME = "com.test.packagename";
+    private static final String TEST_ID = "test_id";
+    private static final String TEST_NAME = "test_name";
+
+    @Mock
+    private MediaRouter2Manager mRouterManager;
+    @Mock
+    private MediaRoute2Info mRouteInfo;
+
+
+    private Context mContext;
+    private InfoMediaDevice mInfoMediaDevice;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+
+        mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
+                TEST_PACKAGE_NAME);
+    }
+
+    @Test
+    public void getName_shouldReturnName() {
+        when(mRouteInfo.getName()).thenReturn(TEST_NAME);
+
+        assertThat(mInfoMediaDevice.getName()).isEqualTo(TEST_NAME);
+    }
+
+    @Test
+    public void getSummary_clientPackageNameIsNull_returnNull() {
+        when(mRouteInfo.getClientPackageName()).thenReturn(null);
+
+        assertThat(mInfoMediaDevice.getSummary()).isEqualTo(null);
+    }
+
+    @Test
+    public void getSummary_clientPackageNameIsNotNull_returnActive() {
+        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaDevice.getSummary())
+                .isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level));
+    }
+
+    @Test
+    public void getId_shouldReturnId() {
+        when(mRouteInfo.getId()).thenReturn(TEST_ID);
+
+        assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
+    }
+
+    @Test
+    public void connect_shouldSelectRoute() {
+        mInfoMediaDevice.connect();
+
+        verify(mRouterManager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo);
+    }
+}
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 b11cf69..67f6dd90 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
@@ -23,9 +23,8 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-
-import androidx.mediarouter.media.MediaRouteSelector;
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -35,6 +34,9 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.ArrayList;
+import java.util.List;
+
 @RunWith(RobolectricTestRunner.class)
 public class InfoMediaManagerTest {
 
@@ -42,9 +44,7 @@
     private static final String TEST_ID = "test_id";
 
     @Mock
-    private MediaRouter mMediaRouter;
-    @Mock
-    private MediaRouteSelector mSelector;
+    private MediaRouter2Manager mRouterManager;
 
     private InfoMediaManager mInfoMediaManager;
     private Context mContext;
@@ -55,82 +55,70 @@
         mContext = RuntimeEnvironment.application;
 
         mInfoMediaManager = new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null);
-        mInfoMediaManager.mMediaRouter = mMediaRouter;
-        mInfoMediaManager.mSelector = mSelector;
+        mInfoMediaManager.mRouterManager = mRouterManager;
     }
 
     @Test
     public void stopScan_shouldRemoveCallback() {
         mInfoMediaManager.stopScan();
 
-        verify(mMediaRouter).removeCallback(mInfoMediaManager.mMediaRouterCallback);
+        verify(mRouterManager).unregisterCallback(mInfoMediaManager.mMediaRouterCallback);
     }
 
     @Test
     public void startScan_shouldAddCallback() {
         mInfoMediaManager.startScan();
 
-        verify(mMediaRouter).addCallback(mSelector, mInfoMediaManager.mMediaRouterCallback,
-                MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+        verify(mRouterManager).registerCallback(mInfoMediaManager.mExecutor,
+                mInfoMediaManager.mMediaRouterCallback);
     }
 
     @Test
-    public void onRouteAdded_mediaDeviceNotExistInList_addMediaDevice() {
-        final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+    public void onRouteAdded_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.onRouteAdded(mMediaRouter, info);
+        mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(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 onRouteAdded_mediaDeviceExistInList_doNothing() {
-        final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
+    public void onControlCategoriesChanged_samePackageName_shouldAddMediaDevice() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
         when(info.getId()).thenReturn(TEST_ID);
-        final InfoMediaDevice infoDevice = new InfoMediaDevice(mContext, info);
-        mInfoMediaManager.mMediaDevices.add(infoDevice);
+        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);
-        final int size = mInfoMediaManager.mMediaDevices.size();
-        assertThat(mediaDevice).isNotNull();
-
-        mInfoMediaManager.mMediaRouterCallback.onRouteAdded(mMediaRouter, info);
-
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(size);
-    }
-
-    @Test
-    public void onRouteRemoved_mediaDeviceExistInList_removeMediaDevice() {
-        final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
-        when(info.getId()).thenReturn(TEST_ID);
-        final InfoMediaDevice infoDevice = new InfoMediaDevice(mContext, info);
-        mInfoMediaManager.mMediaDevices.add(infoDevice);
-
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        assertThat(mediaDevice).isNotNull();
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(1);
-
-        mInfoMediaManager.mMediaRouterCallback.onRouteRemoved(mMediaRouter, info);
-
-        assertThat(mInfoMediaManager.mMediaDevices).isEmpty();
-    }
-
-    @Test
-    public void onRouteRemoved_mediaDeviceNotExistInList_doNothing() {
-        final MediaRouter.RouteInfo info = mock(MediaRouter.RouteInfo.class);
-        when(info.getId()).thenReturn(TEST_ID);
-
-        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
-        final int size = mInfoMediaManager.mMediaDevices.size();
         assertThat(mediaDevice).isNull();
 
-        mInfoMediaManager.mMediaRouterCallback.onRouteRemoved(mMediaRouter, info);
+        mInfoMediaManager.mMediaRouterCallback.onControlCategoriesChanged(TEST_PACKAGE_NAME, null);
 
-        assertThat(mInfoMediaManager.mMediaDevices).hasSize(size);
+        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 onControlCategoriesChanged_differentPackageName_doNothing() {
+        mInfoMediaManager.mMediaRouterCallback.onControlCategoriesChanged("com.fake.play", null);
+
+        assertThat(mInfoMediaManager.mMediaDevices).hasSize(0);
     }
 }
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 23d2c74..02cb83e 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
@@ -22,8 +22,8 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
 
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -56,6 +56,7 @@
     private static final String ROUTER_ID_1 = "RouterId_1";
     private static final String ROUTER_ID_2 = "RouterId_2";
     private static final String ROUTER_ID_3 = "RouterId_3";
+    private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
     private final BluetoothClass mHeadreeClass =
             new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass mCarkitClass =
@@ -76,11 +77,11 @@
     @Mock
     private LocalBluetoothManager mLocalBluetoothManager;
     @Mock
-    private MediaRouter.RouteInfo mRouteInfo1;
+    private MediaRoute2Info mRouteInfo1;
     @Mock
-    private MediaRouter.RouteInfo mRouteInfo2;
+    private MediaRoute2Info mRouteInfo2;
     @Mock
-    private MediaRouter.RouteInfo mRouteInfo3;
+    private MediaRoute2Info mRouteInfo3;
     @Mock
     private LocalBluetoothProfileManager mProfileManager;
     @Mock
@@ -99,6 +100,7 @@
     private InfoMediaDevice mInfoMediaDevice3;
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private PhoneMediaDevice mPhoneMediaDevice;
+    private MediaRouter2Manager mMediaRouter2Manager;
 
     @Before
     public void setUp() {
@@ -134,9 +136,13 @@
         mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1);
         mBluetoothMediaDevice2 = new BluetoothMediaDevice(mContext, mCachedDevice2);
         mBluetoothMediaDevice3 = new BluetoothMediaDevice(mContext, mCachedDevice3);
-        mInfoMediaDevice1 = new InfoMediaDevice(mContext, mRouteInfo1);
-        mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2);
-        mInfoMediaDevice3 = new InfoMediaDevice(mContext, mRouteInfo3);
+        mMediaRouter2Manager = MediaRouter2Manager.getInstance(mContext);
+        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);
     }
 
@@ -364,5 +370,4 @@
         assertThat(mMediaDevices.get(5)).isEqualTo(mBluetoothMediaDevice1);
         assertThat(mMediaDevices.get(6)).isEqualTo(mBluetoothMediaDevice2);
     }
-
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
index 1e5545f..30a6ad2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceUtilsTest.java
@@ -21,8 +21,7 @@
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothDevice;
-
-import androidx.mediarouter.media.MediaRouter;
+import android.media.MediaRoute2Info;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 
@@ -44,7 +43,7 @@
     @Mock
     private BluetoothDevice mBluetoothDevice;
     @Mock
-    private MediaRouter.RouteInfo mRouteInfo;
+    private MediaRoute2Info mRouteInfo;
 
     @Before
     public void setUp() {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 0e3f81b..aa36dca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -266,9 +266,6 @@
         dumpSetting(s, p,
                 Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
                 GlobalSettingsProto.Backup.BACKUP_AGENT_TIMEOUT_PARAMETERS);
-        dumpSetting(s, p,
-                Settings.Global.BACKUP_MULTI_USER_ENABLED,
-                GlobalSettingsProto.Backup.BACKUP_MULTI_USER_ENABLED);
         p.end(backupToken);
 
         final long batteryToken = p.start(GlobalSettingsProto.BATTERY);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4309c80..1e0c1d8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -387,8 +387,10 @@
             case Settings.CALL_METHOD_SET_ALL_CONFIG: {
                 String prefix = getSettingPrefix(args);
                 Map<String, String> flags = getSettingFlags(args);
-                setAllConfigSettings(prefix, flags);
-                break;
+                Bundle result = new Bundle();
+                result.putBoolean(Settings.KEY_CONFIG_SET_RETURN,
+                        setAllConfigSettings(prefix, flags));
+                return result;
             }
 
             case Settings.CALL_METHOD_RESET_CONFIG: {
@@ -2666,20 +2668,28 @@
             return success;
         }
 
+        /**
+         * Set Settings using consumed keyValues, returns true if the keyValues can be set, false
+         * otherwise.
+         */
         public boolean setSettingsLocked(int type, int userId, String prefix,
                 Map<String, String> keyValues, String packageName) {
             final int key = makeKey(type, userId);
 
             SettingsState settingsState = peekSettingsStateLocked(key);
             if (settingsState != null) {
+                if (SETTINGS_TYPE_CONFIG == type && settingsState.isNewConfigBannedLocked(prefix,
+                        keyValues)) {
+                    return false;
+                }
                 List<String> changedSettings =
                         settingsState.setSettingsLocked(prefix, keyValues, packageName);
                 if (!changedSettings.isEmpty()) {
                     notifyForConfigSettingsChangeLocked(key, prefix, changedSettings);
                 }
             }
-
-            return settingsState != null;
+            // keyValues aren't banned and can be set
+            return true;
         }
 
         public boolean deleteSettingLocked(int type, int userId, String name, boolean forceNotify,
@@ -2739,7 +2749,8 @@
 
         public void resetSettingsLocked(int type, int userId, String packageName, int mode,
                 String tag) {
-            resetSettingsLocked(type, userId, packageName, mode, tag, null);
+            resetSettingsLocked(type, userId, packageName, mode, tag, /*prefix=*/
+                    null);
         }
 
         public void resetSettingsLocked(int type, int userId, String packageName, int mode,
@@ -2750,6 +2761,7 @@
                 return;
             }
 
+            banConfigurationIfNecessary(type, prefix, settingsState);
             switch (mode) {
                 case Settings.RESET_MODE_PACKAGE_DEFAULTS: {
                     for (String name : settingsState.getSettingNamesLocked()) {
@@ -3173,6 +3185,34 @@
             return getTypeFromKey(key) == SETTINGS_TYPE_SSAID;
         }
 
+        private boolean shouldBan(int type) {
+            if (SETTINGS_TYPE_CONFIG != type) {
+                return false;
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int appId = UserHandle.getAppId(callingUid);
+
+            // Only non-shell resets should result in namespace banning
+            return appId != SHELL_UID;
+        }
+
+        private void banConfigurationIfNecessary(int type, @Nullable String prefix,
+                SettingsState settingsState) {
+            // Banning should be performed only for Settings.Config and for non-shell reset calls
+            if (!shouldBan(type)) {
+                return;
+            }
+            if (prefix != null) {
+                settingsState.banConfigurationLocked(prefix, getAllConfigFlags(prefix));
+            } else {
+                Set<String> configPrefixes = settingsState.getAllConfigPrefixesLocked();
+                for (String configPrefix : configPrefixes) {
+                    settingsState.banConfigurationLocked(configPrefix,
+                            getAllConfigFlags(configPrefix));
+                }
+            }
+        }
+
         private File getSettingsFile(int key) {
             if (isConfigSettingsKey(key)) {
                 final int userId = getUserIdFromKey(key);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 086b20f..5b1b530 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -65,9 +65,12 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * This class contains the state for one type of settings. It is responsible
@@ -109,6 +112,11 @@
     private static final String ATTR_ID = "id";
     private static final String ATTR_NAME = "name";
 
+    private static final String TAG_NAMESPACE_HASHES = "namespaceHashes";
+    private static final String TAG_NAMESPACE_HASH = "namespaceHash";
+    private static final String ATTR_NAMESPACE = "namespace";
+    private static final String ATTR_BANNED_HASH = "bannedHash";
+
     /**
      * Non-binary value will be written in this attributes.
      */
@@ -159,6 +167,9 @@
     private final ArrayMap<String, Setting> mSettings = new ArrayMap<>();
 
     @GuardedBy("mLock")
+    private final ArrayMap<String, String> mNamespaceBannedHashes = new ArrayMap<>();
+
+    @GuardedBy("mLock")
     private final ArrayMap<String, Integer> mPackageToMemoryUsage;
 
     @GuardedBy("mLock")
@@ -418,6 +429,41 @@
         return true;
     }
 
+    @GuardedBy("mLock")
+    public boolean isNewConfigBannedLocked(String prefix, Map<String, String> keyValues) {
+        // Replaces old style "null" String values with actual null's. This is done to simulate
+        // what will happen to String "null" values when they are written to Settings. This needs to
+        // be done here make sure that config hash computed during is banned check matches the
+        // one computed during banning when values are already stored.
+        keyValues = removeNullValueOldStyle(keyValues);
+        String bannedHash = mNamespaceBannedHashes.get(prefix);
+        if (bannedHash == null) {
+            return false;
+        }
+        return bannedHash.equals(hashCode(keyValues));
+    }
+
+    @GuardedBy("mLock")
+    public void banConfigurationLocked(String prefix, Map<String, String> keyValues) {
+        if (prefix == null || keyValues.isEmpty()) {
+            return;
+        }
+        // The write is intentionally not scheduled here, banned hashes should and will be written
+        // when the related setting changes are written
+        mNamespaceBannedHashes.put(prefix, hashCode(keyValues));
+    }
+
+    @GuardedBy("mLock")
+    public Set<String> getAllConfigPrefixesLocked() {
+        Set<String> prefixSet = new HashSet<>();
+        final int settingsCount = mSettings.size();
+        for (int i = 0; i < settingsCount; i++) {
+            String name = mSettings.keyAt(i);
+            prefixSet.add(name.split("/")[0] + "/");
+        }
+        return prefixSet;
+    }
+
     // The settings provider must hold its lock when calling here.
     // Returns the list of keys which changed (added, updated, or deleted).
     @GuardedBy("mLock")
@@ -710,10 +756,12 @@
         boolean wroteState = false;
         final int version;
         final ArrayMap<String, Setting> settings;
+        final ArrayMap<String, String> namespaceBannedHashes;
 
         synchronized (mLock) {
             version = mVersion;
             settings = new ArrayMap<>(mSettings);
+            namespaceBannedHashes = new ArrayMap<>(mNamespaceBannedHashes);
             mDirty = false;
             mWriteScheduled = false;
         }
@@ -756,8 +804,19 @@
                                 + setting.getValue());
                     }
                 }
-
                 serializer.endTag(null, TAG_SETTINGS);
+
+                serializer.startTag(null, TAG_NAMESPACE_HASHES);
+                for (int i = 0; i < namespaceBannedHashes.size(); i++) {
+                    String namespace = namespaceBannedHashes.keyAt(i);
+                    String bannedHash = namespaceBannedHashes.get(namespace);
+                    writeSingleNamespaceHash(serializer, namespace, bannedHash);
+                    if (DEBUG_PERSISTENCE) {
+                        Slog.i(LOG_TAG, "[PERSISTED] namespace=" + namespace
+                                + ", bannedHash=" + bannedHash);
+                    }
+                }
+                serializer.endTag(null, TAG_NAMESPACE_HASHES);
                 serializer.endDocument();
                 destination.finishWrite(out);
 
@@ -869,6 +928,21 @@
         }
     }
 
+    private static void writeSingleNamespaceHash(XmlSerializer serializer, String namespace,
+            String bannedHashCode) throws IOException {
+        if (namespace == null || bannedHashCode == null) {
+            return;
+        }
+        serializer.startTag(null, TAG_NAMESPACE_HASH);
+        serializer.attribute(null, ATTR_NAMESPACE, namespace);
+        serializer.attribute(null, ATTR_BANNED_HASH, bannedHashCode);
+        serializer.endTag(null, TAG_NAMESPACE_HASH);
+    }
+
+    private static String hashCode(Map<String, String> keyValues) {
+        return Integer.toString(keyValues.hashCode());
+    }
+
     private String getValueAttribute(XmlPullParser parser, String attr, String base64Attr) {
         if (mVersion >= SETTINGS_VERSION_NEW_ENCODING) {
             final String value = parser.getAttributeValue(null, attr);
@@ -939,6 +1013,8 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_SETTINGS)) {
                 parseSettingsLocked(parser);
+            } else if (tagName.equals(TAG_NAMESPACE_HASHES)) {
+                parseNamespaceHash(parser);
             }
         }
     }
@@ -982,6 +1058,37 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private void parseNamespaceHash(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            if (parser.getName().equals(TAG_NAMESPACE_HASH)) {
+                String namespace = parser.getAttributeValue(null, ATTR_NAMESPACE);
+                String bannedHashCode = parser.getAttributeValue(null, ATTR_BANNED_HASH);
+                mNamespaceBannedHashes.put(namespace, bannedHashCode);
+            }
+        }
+    }
+
+    private static Map<String, String> removeNullValueOldStyle(Map<String, String> keyValues) {
+        Iterator<Map.Entry<String, String>> it = keyValues.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, String> keyValueEntry = it.next();
+            if (NULL_VALUE_OLD_STYLE.equals(keyValueEntry.getValue())) {
+                keyValueEntry.setValue(null);
+            }
+        }
+        return keyValues;
+    }
+
     private final class MyHandler extends Handler {
         public static final int MSG_PERSIST_SETTINGS = 1;
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 443811f..7278225 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -571,7 +571,6 @@
                     Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
                     Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
                     Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
-                    Settings.Global.BACKUP_MULTI_USER_ENABLED,
                     Settings.Global.ISOLATED_STORAGE_LOCAL,
                     Settings.Global.ISOLATED_STORAGE_REMOTE,
                     Settings.Global.APPOP_HISTORY_PARAMETERS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index aefdce4..347d6c2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -230,6 +230,9 @@
     <!-- Permission required for CTS test - CtsOsTestCases -->
     <uses-permission android:name="android.permission.MANAGE_CRATES"/>
 
+    <!-- Allows setting brightness from the shell -->
+    <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 52ec1f0..0a2dd6c 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -30,7 +30,7 @@
  */
 @ProvidesInterface(version = FalsingManager.VERSION)
 public interface FalsingManager {
-    int VERSION = 2;
+    int VERSION = 3;
 
     void onSucccessfulUnlock();
 
@@ -48,7 +48,7 @@
 
     void setNotificationExpanded();
 
-    boolean isClassiferEnabled();
+    boolean isClassifierEnabled();
 
     void onQsDown();
 
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index efe4088..393da27 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -74,7 +74,7 @@
     <string name="kg_pattern_instructions" msgid="5376036737065051736">"ارسم نقشك"</string>
     <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"‏أدخل رقم التعريف الشخصي لشريحة SIM."</string>
     <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"‏أدخل رقم التعريف الشخصي لشريحة SIM التابعة للمشغّل \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string>
-    <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"‏<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> يجب إيقاف eSIM لاستخدام الجهاز دون خدمة جوّال."</string>
+    <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"‏<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> يجب إيقاف eSIM لاستخدام الجهاز بدون خدمة الأجهزة الجوّالة."</string>
     <string name="kg_pin_instructions" msgid="822353548385014361">"أدخل رقم التعريف الشخصي"</string>
     <string name="kg_password_instructions" msgid="324455062831719903">"أدخل كلمة المرور"</string>
     <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"‏شريحة SIM غير مفعّلة الآن. أدخل رمز PUK للمتابعة. اتصل بمشغل شبكة الجوّال للاطلاع على التفاصيل."</string>
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index f7e9fed..4d184d5 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -240,6 +240,15 @@
     <!-- Description of airplane mode -->
     <string name="airplane_mode">Airplane mode</string>
 
+    <!-- An explanation text that the PIN needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_prepare_for_update_pin">PIN required to prepare for update</string>
+
+    <!-- An explanation text that the pattern needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_prepare_for_update_pattern">Pattern required to prepare for update</string>
+
+    <!-- An explanation text that the password needs to be entered to prepare for an operating system update. [CHAR LIMIT=80] -->
+    <string name="kg_prompt_reason_prepare_for_update_password">Password required to prepare for update</string>
+
     <!-- An explanation text that the pattern needs to be solved since the device has just been restarted. [CHAR LIMIT=80] -->
     <string name="kg_prompt_reason_restart_pattern">Pattern required after device restarts</string>
 
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 4869be1..479f255 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -17,9 +17,14 @@
 */
 -->
 
-<merge
+
+<com.android.systemui.statusbar.phone.NotificationPanelView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto">
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/notification_panel"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/transparent">
     <FrameLayout
         android:id="@+id/big_clock_container"
         android:layout_width="match_parent"
@@ -97,4 +102,4 @@
         android:background="@drawable/qs_navbar_scrim" />
 
     <include layout="@layout/status_bar_expanded_plugin_frame"/>
-</merge>
+</com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 57834da..9716a00 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -64,7 +64,7 @@
         sysui:ignoreRightInset="true"
         />
 
-    <ViewStub android:id="@+id/status_bar_expanded"
+    <include layout="@layout/status_bar_expanded"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:visibility="invisible" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 08be42a..ea7b9bb 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Skakel Batterybespaarder af"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sal toegang hê tot al die inligting wat op jou skerm sigbaar is of wat op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto\'s, boodskappe en oudio wat jy speel."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Die diens wat hierdie funksie verskaf, sal toegang hê tot al die inligting wat op jou skerm sigbaar is of wat op jou toestel gespeel word terwyl dit opneem of uitsaai. Dit sluit in inligting soos wagwoorde, betalingbesonderhede, foto\'s, boodskappe en oudio wat jy speel."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Begin opneem of uitsaai?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Begin opneem of uitsaai met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Moenie weer wys nie"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Vee alles uit"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 18a4a7a..9412f0f 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ባትሪ ቆጣቢን አጥፋ"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> በእርስዎ ማያ ገጽ ላይ ያለን ወይም በእርስዎ መሣሪያ ላይ በመጫወት ላይ ያለን ሁሉንም መረጃ በቀረጻ ወይም casting ላይ እያለ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚጫውቱት ኦዲዮን የመሳሰለ መረጃን ያካትታል።"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"ይህን ተግባር የሚያቀርበው አገልግሎት በእርስዎ ማያ ገጽ ላይ ያለን ወይም በእርስዎ መሣሪያ ላይ በመጫወት ላይ ያለን ሁሉንም መረጃ በቀረጻ ወይም casting ላይ እያለ መዳረሻ ይኖረዋል። ይህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ ፎቶዎች፣ መልዕክቶች እና እርስዎ የሚጫውቱት ኦዲዮን የመሳሰለ መረጃን ያካትታል።"</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ቀረጻ ወይም cast ማድረግ ይጀምር?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"ከ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ጋር ቀረጻ ወይም casting ይጀምር?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"ዳግመኛ አታሳይ"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ሁሉንም አጽዳ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 07be692..f675265 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -111,7 +111,7 @@
     <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"المساعد الصوتي"</string>
     <string name="accessibility_unlock_button" msgid="122785427241471085">"إلغاء القفل"</string>
     <string name="accessibility_waiting_for_fingerprint" msgid="5209142744692162598">"في انتظار بصمة الإصبع"</string>
-    <string name="accessibility_unlock_without_fingerprint" msgid="1811563723195375298">"إلغاء القفل دون استخدام بصمة إصبعك"</string>
+    <string name="accessibility_unlock_without_fingerprint" msgid="1811563723195375298">"فتح القفل بدون استخدام بصمة إصبعك"</string>
     <string name="accessibility_scanning_face" msgid="3093828357921541387">"مسح الوجه"</string>
     <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"إرسال"</string>
     <string name="accessibility_manage_notification" msgid="582215815790143983">"إدارة الإشعارات"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 14f5e7f..2b94d67 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -468,8 +468,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključi Uštedu baterije"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje se prikazuju na ekranu ili reprodukuju sa uređaja tokom snimanja ili prebacivanja. To obuhvata informacije poput lozinki, informacija o plaćanju, slika, poruka i zvuka koji puštate."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Želite da počnete snimanje ili prebacivanje?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Želite da počnete snimanje ili prebacivanje pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj ponovo"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Obriši sve"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 9b7b62a..26102fb 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Изключване на режима за запазване на батерията"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, данни за плащане, снимки, съобщения и възпроизвеждано аудио."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата, предоставяща тази функция, ще има достъп до цялата информация, която е видима на екрана или възпроизвеждана от устройството ви по време на записване или предаване. Това включва различна информация, като например пароли, данни за плащане, снимки, съобщения и възпроизвеждано аудио."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Да се стартира ли записване или предаване?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Да се стартира ли записване или предаване чрез <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Да не се показва отново"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Изчистване на всички"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 6e2285e..5851011 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -468,8 +468,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključi Uštedu baterije"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Aplikacija <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> će imati pristup svim informacijama koje se prikazuju na ekranu ili koje se reproduciraju s vašeg uređaja za vrijeme snimanja ili emitiranja. To obuhvata informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkciju će imati pristup svim informacijama koje se prikazuju na ekranu ili koje se reproduciraju s vašeg uređaja za vrijeme snimanja ili emitiranja. To obuhvata informacije kao što su lozinke, detalji o plaćanju, fotografije, poruke i zvuk koji reproducirate."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Započeti snimanje ili emitiranje?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranje s aplikacijom <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj opet"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Očisti sve"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 4909e10..9fb7230 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactiva l\'estalvi de bateria"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tindrà accés a tota la informació que es veu en pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara contrasenyes, detalls dels pagaments, fotos, missatges i àudio."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servei que ofereix aquesta funció tindrà accés a tota la informació que es veu en pantalla o que es reprodueix al dispositiu mentre graves o emets contingut, com ara contrasenyes, detalls dels pagaments, fotos, missatges i àudio."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vols començar a gravar o emetre contingut?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Vols començar a gravar o emetre contingut amb <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"No ho tornis a mostrar"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Esborra-ho tot"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index b5b8aa3..30724f8 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -471,8 +471,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Vypnout spořič baterie"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Aplikace <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bude mít přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze za řízení při nahrávání nebo odesílání. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Služba, která poskytuje tuto funkci, bude mít přístup ke všem informacím, které jsou viditelné na obrazovce nebo které jsou přehrávány ze zařízení při nahrávání nebo odesílání. Týká se to i hesel, údajů o platbě, fotek, zpráv a přehrávaných zvuků."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Začít nahrávat nebo odesílat?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Začít nahrávat nebo odesílat s aplikací <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Tuto zprávu příště nezobrazovat"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Smazat vše"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 80c8d28..e20a276 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Deaktiver batterisparefunktion"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Tjenesten, der tilbyder denne funktion, får adgang til alle de oplysninger, der er synlige på din skærm, eller som afspilles på din enhed, når du optager eller caster. Dette omfatter oplysninger som f.eks. adgangskoder, betalingsoplysninger, billeder, beskeder og afspillet lyd."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vil du begynde at optage eller caste?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Vil du begynde at optage eller caste via <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Vis ikke igen"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Ryd alle"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 8a71fb2..14da101 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Απενεργοποίηση Εξοικονόμησης μπαταρίας"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Η εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Η υπηρεσία που παρέχει αυτήν τη λειτουργία θα έχει πρόσβαση σε όλες τις πληροφορίες που εμφανίζονται στην οθόνη σας ή που αναπαράγονται από τη συσκευή σας κατά την εγγραφή ή τη μετάδοση. Αυτό περιλαμβάνει πληροφορίες όπως κωδικούς πρόσβασης, στοιχεία πληρωμής, φωτογραφίες, μηνύματα και ήχο που αναπαράγετε."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Έναρξη εγγραφής ή μετάδοσης;"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Έναρξη εγγραφής ή μετάδοσης με <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>;"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Να μην εμφανιστεί ξανά"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Διαγραφή όλων"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 1ffd1b1..5f38830 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 652353c..1da1df3 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 1ffd1b1..5f38830 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 1ffd1b1..5f38830 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Turn off Battery Saver"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information, such as passwords, payment details, photos, messages and audio that you play."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Start recording or casting?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Start recording or casting with <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Clear all"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index cc5ae64..fd3ff45 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‎‏‎‎‏‎‏‏‎‎‎‎‎‏‏‎‏‏‏‏‎‎‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎Turn off Battery Saver‎‏‎‎‏‎"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎ will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.‎‏‎‎‏‎"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‎‏‏‎‏‎‎‏‏‏‎‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎The service providing this function will have access to all of the information that is visible on your screen or played from your device while recording or casting. This includes information such as passwords, payment details, photos, messages, and audio that you play.‎‏‎‎‏‎"</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‎‎‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‏‎‎‎‎‎‏‎‎‎‎Start recording or casting?‎‏‎‎‏‎"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎Start recording or casting with ‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎?‎‏‎‎‏‎"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎Don\'t show again‎‏‎‎‏‎"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎Clear all‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 713a996..eb27998f 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactivar el Ahorro de batería"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en tu dispositivo durante una grabación o transmisión. Se incluyen las contraseñas, los detalles del pago, las fotos, los mensajes y el audio que reproduzcas."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que brinda esta función tendrá acceso a toda la información que sea visible en la pantalla o que reproduzcas en tu dispositivo durante una grabación o transmisión. Se incluyen las contraseñas, los detalles del pago, las fotos, los mensajes y el audio que reproduzcas."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"¿Deseas comenzar a grabar o transmitir contenido?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Deseas iniciar una grabación o transmisión con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"No volver a mostrar"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index ffcafae..731d727 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desactivar Ahorro de batería"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> tendrá acceso a toda la información que se muestra en pantalla o se reproduce en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"El servicio que ofrece esta función tendrá acceso a toda la información que se muestra en pantalla o se reproduce en el dispositivo mientras grabas o envías contenido, incluyendo contraseñas, detalles de pagos, fotos, mensajes y audios que reproduzcas."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"¿Quieres empezar a grabar o enviar contenido?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"¿Quieres iniciar la grabación o el envío de contenido con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"No volver a mostrar"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Borrar todo"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index db87961..ab86e03 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Akusäästja väljalülitamine"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Rakendus <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Seda funktsiooni pakkuv teenus saab juurdepääsu kogu teabele, mis on teie ekraanikuval nähtav või mida seadmes salvestamise või ülekande ajal esitatakse. See hõlmab teavet, nagu paroolid, maksete üksikasjad, fotod, sõnumid ja esitatav heli."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Kas alustada salvestamist või ülekannet?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Kas alustada rakendusega <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> salvestamist või ülekannet?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ära kuva uuesti"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tühjenda kõik"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 51e3d2b..18ac031 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desaktibatu bateria-aurrezlea"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Zerbait grabatzen edo igortzen duzunean, pantailan ikus daitekeen edo gailuak erreproduzitzen duen informazio guztia atzitu ahalko du <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Horrek barne hartzen ditu pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Zerbait grabatzen edo igortzen duzunean, pantailan ikus daitekeen edo gailuak erreproduzitzen duen informazio guztia atzitu ahalko du funtzio hori eskaintzen duen zerbitzuak. Horrek barne hartzen ditu pasahitzak, ordainketen xehetasunak, argazkiak, mezuak eta erreproduzitzen dituzun audioak."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Grabatzen edo igortzen hasi nahi duzu?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioarekin grabatzen edo igortzen hasi nahi duzu?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ez erakutsi berriro"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Garbitu guztiak"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 20b5565..77164b7 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"«بهینه‌سازی باتری» را خاموش کنید"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> به همه اطلاعاتی که روی صفحه‌نمایش قابل‌مشاهد است و هنگام ضبط کردن یا ارسال محتوا از دستگاهتان پخش می‌شود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژه‌ها، جزئیات پرداخت، عکس‌ها، پیام‌ها، و صداهایی که پخش می‌کنید می‌شود."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"سرویس ارائه‌دهنده این عملکرد به همه اطلاعاتی که روی صفحه‌نمایش قابل‌مشاهد است و هنگام ضبط کردن یا ارسال محتوا از دستگاهتان پخش می‌شود دسترسی خواهد داشت. این شامل اطلاعاتی مانند گذرواژه‌ها، جزئیات پرداخت، عکس‌ها، پیام‌ها، و صداهایی که پخش می‌کنید می‌شود."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ضبط یا ارسال محتوا شروع شود؟"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"ضبط یا ارسال محتوا با <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> شروع شود؟"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"دوباره نشان داده نشود"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"پاک کردن همه موارد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 945e63d..e70b4d5 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Poista virransäästö käytöstä"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> saa pääsyn kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Ominaisuuden tarjoavalla palvelulla on pääsy kaikkiin näytölläsi näkyviin tietoihin ja tietoihin laitteesi toistamasta sisällöstä tallennuksen tai striimauksen aikana. Näitä tietoja ovat esimerkiksi salasanat, maksutiedot, kuvat, viestit ja toistettava audiosisältö."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aloitetaanko tallentaminen tai striimaus?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Haluatko, että <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aloittaa tallennuksen tai striimauksen?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Älä näytä uudelleen"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Poista kaikki"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index ac27ea8..ead29c0 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver la fonction Économie d\'énergie"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et l\'audio que vous faites jouer."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service offrant cette fonction aura accès à toute l\'information visible sur votre écran ou qui joue sur votre appareil durant l\'enregistrement ou la diffusion. Cela comprend des renseignements comme les mots de passe, les détails du paiement, les photos, les messages et l\'audio que vous faites jouer."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Commencer à enregistrer ou à diffuser?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Commencer à enregistrer ou à diffuser avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne plus afficher"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index d88201d..b701276 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Désactiver l\'économiseur de batterie"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Par exemple, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore vos contenus audio lus."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Le service fournissant cette fonctionnalité aura accès à toutes les informations visibles sur votre écran ou lues depuis votre appareil lors d\'un enregistrement ou d\'une diffusion de contenu. Par exemple, vos mots de passe, vos données de paiement, vos photos, vos messages ou encore vos contenus audio lus."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Démarrer l\'enregistrement ou la diffusion ?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Démarrer l\'enregistrement ou la diffusion avec <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne plus afficher"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Tout effacer"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 0cbb0bc..de6b3793 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"બૅટરી સેવર બંધ કરો"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"રેકૉર્ડ અથવા કાસ્ટ કરતી વખતે, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. આમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, સંદેશા અને તમે ચલાવો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"રેકૉર્ડ અથવા કાસ્ટ કરતી વખતે, આ સુવિધા આપતી સેવાને તમારી સ્ક્રીન પર દેખાતી હોય અથવા તમારા ડિવાઇસ પર ચલાવવામાં આવતી હોય તેવી બધી માહિતીનો ઍક્સેસ હશે. આમાં પાસવર્ડ, ચુકવણીની વિગતો, ફોટા, સંદેશા અને તમે ચલાવો છો તે ઑડિયો જેવી માહિતીનો સમાવેશ થાય છે."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"શું રેકૉર્ડ અથવા કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> વડે રેકૉર્ડ અથવા કાસ્ટ કરવાનું શરૂ કરીએ?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"ફરીથી બતાવશો નહીં"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"બધુ સાફ કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 23a5126..706f8b3 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -946,10 +946,7 @@
     <string name="notification_content_system_nav_changed" msgid="5077913144844684544">"सिस्टम नेविगेशन अपडेट हो गया. बदलाव करने के लिए \'सेटिंग\' पर जाएं."</string>
     <string name="notification_content_gesture_nav_available" msgid="4431460803004659888">"सिस्टम नेविगेशन अपडेट करने के लिए \'सेटिंग\' में जाएं"</string>
     <string name="inattentive_sleep_warning_title" msgid="3891371591713990373">"स्टैंडबाई"</string>
-    <!-- no translation found for magnification_overlay_title (6584179429612427958) -->
-    <skip />
-    <!-- no translation found for magnification_window_title (4863914360847258333) -->
-    <skip />
-    <!-- no translation found for magnification_controls_title (8421106606708891519) -->
-    <skip />
+    <string name="magnification_overlay_title" msgid="6584179429612427958">"Magnification Overlay Window"</string>
+    <string name="magnification_window_title" msgid="4863914360847258333">"स्क्रीन को बड़ा करके दिखाने वाली विंडो"</string>
+    <string name="magnification_controls_title" msgid="8421106606708891519">"स्क्रीन को बड़ा करके दिखाने वाली विंडो के नियंत्रण"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 42f0df5..bf027fb 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -468,8 +468,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Isključite Štednju baterije"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Usluga koja pruža ovu funkcionalnost imat će pristup svim podacima koji su vidljivi na vašem zaslonu ili koji se reproduciraju s vašeg uređaja tijekom snimanja ili emitiranja. To uključuje podatke kao što su zaporke, podaci o plaćanju, fotografije, poruke i audiozapisi koje reproducirate."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Započeti snimanje ili emitiranje?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Započeti snimanje ili emitiranja pomoću aplikacije <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne prikazuj ponovo"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Izbriši sve"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 7a5eadf..90c8a1a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Akkumulátorkímélő mód kikapcsolása"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"A(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, vagy amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés során. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, fotók, üzenetek és lejátszott audiotartalmak."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"A funkciót biztosító szolgáltatás hozzáfér majd minden olyan információhoz, amely látható az Ön képernyőjén, illetve amelyet az Ön eszközéről játszanak le rögzítés vagy átküldés közben. Ez olyan információkat is tartalmaz, mint a jelszavak, a fizetési részletek, fotók, üzenetek és lejátszott audiotartalmak."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Biztosan elkezdi a rögzítést vagy az átküldést?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Elkezdi a rögzítést vagy átküldést a következővel: <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ne jelenjen meg többé"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Az összes törlése"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 9f24d74..95e4010 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Slökkva á rafhlöðusparnaði"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> mun hafa aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða útsending er í gangi. Þar á meðal eru upplýsingar á borð við aðgangsorð, greiðsluupplýsingar, myndir, skilaboð og hljóð sem þú spilar."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Þjónustan sem býður upp á þennan eiginleika mun hafa aðgang að öllum upplýsingum sem sjást á skjánum eða eru spilaðar í tækinu á meðan upptaka eða útsending er í gangi. Þar á meðal eru upplýsingar á borð við aðgangsorð, greiðsluupplýsingar, myndir, skilaboð og hljóð sem þú spilar."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Viltu hefja upptöku eða útsendingu?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Viltu hefja upptöku eða útsendingu með <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ekki sýna þetta aftur"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Hreinsa allt"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9b2faa5..4cec980 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Disattiva Risparmio energetico"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Il servizio che offre questa funzione avrà accesso a tutte le informazioni visibili sul tuo schermo o riprodotte dal tuo dispositivo durante la registrazione o la trasmissione. Sono incluse informazioni quali password, dettagli sui pagamenti, foto, messaggi e audio riprodotto."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vuoi avviare la registrazione o la trasmissione?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Vuoi avviare la registrazione o la trasmissione con <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Non mostrare più"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Cancella tutto"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 474327d..949c8db 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -471,8 +471,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"כיבוי תכונת החיסכון בסוללה"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"‏לאפליקציה <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"‏לשירות שמספק את הפונקציה הזו תהיה גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך בזמן הקלטה או העברה (cast). זה כולל פרטים כמו סיסמאות, פרטי תשלום, תמונות, הודעות ואודיו שמושמע מהמכשיר."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"‏להתחיל להקליט או להעביר (cast)?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"‏להתחיל להקליט או להעביר (cast) באמצעות <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"אל תציג שוב"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"ניקוי הכל"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 60b4fe4..49d678b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -471,8 +471,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Išjungti Akumuliatoriaus tausojimo priemonę"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"„<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Šią funkcija teikianti paslauga galės pasiekti visą informaciją, matomą ekrane ir leidžiamą iš įrenginio įrašant ar perduodant turinį. Tai apima įvairią informaciją, pvz., slaptažodžius, išsamią mokėjimo informaciją, nuotraukas, pranešimus ir leidžiamus garso įrašus."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Pradėti įrašyti ar perduoti turinį?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Pradėti įrašyti ar perduoti turinį naudojant „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Daugiau neberodyti"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Viską išvalyti"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 5233b16..0294a1f 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Исклучете го штедачот на батерија"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услугата што ја обезбедува функцијава ќе има пристап до сите информации што се видливи на екранот или пуштени од вашиот уред додека се снима или емитува. Ова вклучува информации како, на пример, лозинки, детали на исплатата, фотографии, пораки и аудио што го пуштате."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Да почне снимање или емитување?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Да почне снимање или емитување со <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Не покажувај повторно"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Избриши сѐ"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 862f63e..e5497ea 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Тэжээл хэмнэгчийг унтраах"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэц дээр харагдах эсвэл таны төхөөрөмжөөс тоглуулах бүх мэдээлэлд хандах боломжтой байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Энэ функцийг ажиллуулж байгаа үйлчилгээ нь бичлэг хийх эсвэл дамжуулах үед таны дэлгэц дээр харагдах эсвэл таны төхөөрөмжөөс тоглуулах бүх мэдээлэлд хандах боломжтой байна. Үүнд нууц үг, төлбөрийн дэлгэрэнгүй, зураг болон таны тоглуулдаг аудио зэрэг мэдээлэл багтана."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Бичлэг хийх эсвэл дамжуулахыг эхлүүлэх үү?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-тай бичлэг хийж эсвэл дамжуулж эхлэх үү?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Дахиж үл харуулах"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Бүгдийг арилгах"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9a8b50fc..b632bcd 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Matikan Penjimat Bateri"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> akan mempunyai akses kepada semua maklumat yang kelihatan pada skrin anda atau yang dimainkan daripada peranti anda semasa merakam atau membuat penghantaran. Ini termasuklah maklumat seperti kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Perkhidmatan yang menyediakan fungsi ini akan mempunyai akses kepada semua maklumat yang kelihatan pada skrin anda atau dimainkan daripada peranti anda semasa merakam atau membuat penghantaran. Ini termasuklah maklumat seperti kata laluan, butiran pembayaran, foto, mesej dan audio yang anda mainkan."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Mulakan rakaman atau penghantaran?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Mulakan rakaman atau penghantaran dengan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Jangan tunjukkan lagi"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Kosongkan semua"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index ef6bc47..ba6b0ed 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"ब्याट्री सेभर निष्क्रिय पार्नुहोस्"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"यो कार्य प्रदान गर्ने सेवाले तपाईंको स्क्रिनमा देख्न सकिने सबै जानकारी अथवा रेकर्ड वा cast गर्दा तपाईंको यन्त्रबाट प्ले गरिएका कुरामाथि पहुँच राख्न सक्ने छ। यसअन्तर्गत पासवर्ड, भुक्तानीका विवरण, तस्बिर, सन्देश र तपाईंले प्ले गर्ने अडियो जस्ता जानकारी समावेश हुन्छन्।"</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मार्फत रेकर्ड गर्न वा cast गर्न थाल्ने हो?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"फेरि नदेखाउनुहोस्"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"सबै हटाउनुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5d57927..638fcad 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Batterijbesparing uitschakelen"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> krijgt toegang tot alle informatie die zichtbaar is op je scherm of die wordt afgespeeld vanaf je apparaat tijdens het opnemen of casten. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"De service die deze functie levert, krijgt toegang tot alle informatie die zichtbaar is op je scherm of die wordt afgespeeld vanaf je apparaat tijdens het opnemen of casten. Dit omvat informatie zoals wachtwoorden, betalingsgegevens, foto\'s, berichten en audio die je afspeelt."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Beginnen met opnemen of casten?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Beginnen met opnemen of casten met <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Niet opnieuw weergeven"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Alles wissen"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index d3f5ae2..307d417 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -471,8 +471,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Wyłącz Oszczędzanie baterii"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Podczas nagrywania i przesyłania aplikacja <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> będzie mieć dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Podczas nagrywania i przesyłania usługa udostępniająca tę funkcję będzie miała dostęp do wszystkich informacji widocznych na ekranie lub odtwarzanych na urządzeniu. Dotyczy to m.in. haseł, szczegółów płatności, zdjęć, wiadomości i odtwarzanych dźwięków."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Rozpocząć nagrywanie lub przesyłanie?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Rozpocząć nagrywanie lub przesyłanie za pomocą aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Nie pokazuj ponownie"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Usuń wszystkie"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4d4a607..c1dd243 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo durante uma gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Não mostrar novamente"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1f23159..9a5a4b3 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Poupança de bateria"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"A aplicação <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que fornece esta função terá acesso a todas as informações que estiverem visíveis no ecrã ou que forem reproduzidas a partir do dispositivo durante a gravação ou transmissão. Isto inclui informações como palavras-passe, detalhes de pagamentos, fotos, mensagens e áudio reproduzido."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Pretende começar a gravar ou a transmitir?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Pretende começar a gravar ou a transmitir com a aplicação <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Não mostrar de novo"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4d4a607..c1dd243 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Desativar a Economia de bateria"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"O app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo, como gravação ou transmissão Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"O serviço que oferece essa função terá acesso a todas as informações visíveis na tela ou tocadas no dispositivo durante uma gravação ou transmissão. Isso inclui informações como senhas, detalhes de pagamento, fotos, mensagens e áudio que você toca."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Iniciar gravação ou transmissão?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Iniciar gravação ou transmissão com o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Não mostrar novamente"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Limpar tudo"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 6fc30de..c38c855 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -471,8 +471,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Vypnúť šetrič batérie"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Povolenie <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, akými sú napríklad heslá, platobné podrobnosti, fotky, správy a prehrávaný zvuk."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Služba poskytujúca túto funkciu bude mať prístup k všetkým informáciám zobrazovaným na obrazovke alebo prehrávaným v zariadení počas nahrávania či prenosu. Patria medzi ne informácie, akými sú napríklad heslá, platobné podrobnosti, fotky, správy a prehrávaný zvuk."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Chcete začať nahrávanie alebo prenos?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Chcete spustiť nahrávanie alebo prenos s aktivovaným povolením <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Nabudúce nezobrazovať"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Vymazať všetko"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 730f4b0..445f8ca4 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Çaktivizo \"Kursyesin e baterisë\""</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Shërbimi që e ofron këtë funksion do të ketë qasje te të gjitha informacionet që janë të dukshme në ekran ose që luhen nga pajisja jote gjatë regjistrimit ose transmetimit. Kjo përfshin informacione si p.sh. fjalëkalimet, detajet e pagesave, fotografitë, mesazhet dhe audion që luan ti."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Do të fillosh regjistrimin ose transmetimin?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Fillo regjistrimin ose transmetimin me <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Mos e shfaq sërish"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Pastroji të gjitha"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3660b06..acff269 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -468,8 +468,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Искључи Уштеду батерије"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Услуга која пружа ову функцију ће имати приступ свим информацијама које се приказују на екрану или репродукују са уређаја током снимања или пребацивања. То обухвата информације попут лозинки, информација о плаћању, слика, порука и звука који пуштате."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Желите да почнете снимање или пребацивање?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Желите да почнете снимање или пребацивање помоћу апликације <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Не приказуј поново"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Обриши све"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 67e2e04..d63206a 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Inaktivera batterisparläget"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar uppgifter som lösenord, betalningsinformation, foton, meddelanden och ljud som du spelar upp."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Den tjänst som tillhandahåller funktionen får åtkomst till all information som visas på skärmen eller spelas upp från enheten när du spelar in eller castar. Detta omfattar uppgifter som lösenord, betalningsinformation, foton, meddelanden och ljud som du spelar upp."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Vill du börja spela in eller casta?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Vill du börja spela in eller casta med <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Visa inte igen"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Rensa alla"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 94377d7..3640c80 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -471,8 +471,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Вимкнути режим економії заряду акумулятора"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"Додаток <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> матиме доступ до всієї інформації, яка з\'являється на екрані або відтворюється на пристрої під час запису чи трансляції, зокрема до паролів, інформації про платежі, фотографій, повідомлень і аудіофайлів."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Сервіс, що надає цю функцію, матиме доступ до всієї інформації, яка з\'являється на екрані або відтворюється на пристрої під час запису чи трансляції, зокрема до паролів, інформації про платежі, фотографій, повідомлень і аудіофайлів."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Почати запис або трансляцію?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Почати запис або трансляцію за допомогою додатка <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Більше не показувати"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Очистити все"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index ec11d1f..fdc618b 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"بیٹری سیور آف کریں"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کو ان تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر مرئی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائے گئے ہوں۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات، اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"یہ فنکشن فراہم کرنے والی سروس کو ان تمام معلومات تک رسائی حاصل ہوگی جو آپ کی اسکرین پر مرئی ہے یا ریکارڈنگ یا کاسٹنگ کے دوران آپ کے آلے سے چلائے گئے ہوں۔ اس میں پاس ورڈز، ادائیگی کی تفصیلات، تصاویر، پیغامات، اور آپ کے ذریعے چلائی جانے والی آڈیو جیسی معلومات شامل ہے۔"</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"ریکارڈنگ یا کاسٹنگ شروع کریں؟"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کے ذریعے ریکارڈنگ یا کاسٹنگ شروع کریں؟"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"دوبارہ نہ دکھائیں"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"سبھی کو صاف کریں"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 10348fb..dae6eb1 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"關閉省電模式"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"在錄影或投放時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可以存取螢幕顯示或裝置播放的任何資料,當中包括密碼、付款詳情、相片、訊息和播放的語音等。"</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"在錄影或投放時,此功能的服務供應商可以存取螢幕顯示或裝置播放的任何資料,當中包括密碼、付款詳情、相片、訊息和播放的語音等。"</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"要開始錄影或投放嗎?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"要使用「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」開始錄影或投放嗎?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"不用再顯示"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"全部清除"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 7ae14cc..5353018 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -465,8 +465,7 @@
     <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Vala isilondolozi sebhethri"</string>
     <string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> izithola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma idlalwe kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
     <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Isevisi enikezela ngalo msebenzi izothola ukufinyelela kulo lonke ulwazi olubonakalayo esikrinini sakho noma oludlalwa kusuka kudivayisi yakho ngenkathi urekhoda noma usakaza. Lokhu kubandakanya ulwazi olufana namaphasiwedi, imininingwane yenkokhelo, izithombe, imilayezo, nomsindo owudlalayo."</string>
-    <!-- no translation found for media_projection_dialog_service_title (2888507074107884040) -->
-    <skip />
+    <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Qala ukurekhoda noma ukusakaza?"</string>
     <string name="media_projection_dialog_title" msgid="3316063622495360646">"Qala ukurekhoda noma ukusakaza nge-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
     <string name="media_projection_remember_text" msgid="6896767327140422951">"Ungabonisi futhi"</string>
     <string name="clear_all_notifications_text" msgid="348312370303046130">"Sula konke"</string>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
deleted file mode 100644
index 49b87f3..0000000
--- a/packages/SystemUI/res/values/attrs_car.xml
+++ /dev/null
@@ -1,103 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <attr name="icon" format="reference"/>
-    <attr name="selectedIcon" format="reference"/>
-    <attr name="intent" format="string"/>
-    <attr name="longIntent" format="string"/>
-    <attr name="selectedAlpha" format="float" />
-    <attr name="unselectedAlpha" format="float" />
-
-    <!-- Allow for custom attribs to be added to a facet button -->
-    <declare-styleable name="CarFacetButton">
-        <!-- icon to be rendered (drawable) -->
-        <attr name="icon"/>
-        <!-- icon to be rendered when in selected state -->
-        <attr name="selectedIcon"/>
-        <!-- intent to start when button is click -->
-        <attr name="intent"/>
-        <!-- intent to start when a long press has happened -->
-        <attr name="longIntent"/>
-        <!-- categories that will be added as extras to the fired intents -->
-        <attr name="categories" format="string"/>
-        <!-- package names that will be added as extras to the fired intents -->
-        <attr name="packages" format="string" />
-        <!-- componentName names that will be used for detecting selected state -->
-        <attr name="componentNames" format="string" />
-        <!-- Alpha value to used when in selected state.  Defaults 1f  -->
-        <attr name="selectedAlpha" />
-        <!-- Alpha value to used when in un-selected state.  Defaults 0.7f  -->
-        <attr name="unselectedAlpha" />
-        <!-- Render a "more" icon. Defaults true  -->
-        <attr name="useMoreIcon" format="boolean" />
-
-    </declare-styleable>
-
-
-    <!-- Allow for custom attribs to be added to a nav button -->
-    <declare-styleable name="CarNavigationButton">
-        <!-- intent to start when button is click -->
-        <attr name="intent" />
-        <!-- intent to start when a long press has happened -->
-        <attr name="longIntent" />
-        <!-- start the intent as a broad cast instead of an activity if true-->
-        <attr name="broadcast" format="boolean"/>
-        <!-- Alpha value to used when in selected state.  Defaults 1f  -->
-        <attr name="selectedAlpha" />
-        <!-- Alpha value to used when in un-selected state.  Defaults 0.7f  -->
-        <attr name="unselectedAlpha" />
-        <!-- icon to be rendered when in selected state -->
-        <attr name="selectedIcon" />
-        <!-- icon to be rendered (drawable) -->
-        <attr name="icon"/>
-    </declare-styleable>
-
-    <!-- Custom attributes to configure hvac values -->
-    <declare-styleable name="TemperatureView">
-        <attr name="hvacAreaId" format="integer"/>
-        <attr name="hvacPropertyId" format="integer"/>
-        <attr name="hvacTempFormat" format="string"/>
-    </declare-styleable>
-
-    <declare-styleable name="carVolumeItems"/>
-    <declare-styleable name="carVolumeItems_item">
-        <!-- Align with AudioAttributes.USAGE_* -->
-        <attr name="usage">
-            <enum name="unknown" value="0"/>
-            <enum name="media" value="1"/>
-            <enum name="voice_communication" value="2"/>
-            <enum name="voice_communication_signalling" value="3"/>
-            <enum name="alarm" value="4"/>
-            <enum name="notification" value="5"/>
-            <enum name="notification_ringtone" value="6"/>
-            <enum name="notification_communication_request" value="7"/>
-            <enum name="notification_communication_instant" value="8"/>
-            <enum name="notification_communication_delayed" value="9"/>
-            <enum name="notification_event" value="10"/>
-            <enum name="assistance_accessibility" value="11"/>
-            <enum name="assistance_navigation_guidance" value="12"/>
-            <enum name="assistance_sonification" value="13"/>
-            <enum name="game" value="14"/>
-            <!-- hidden, do not use -->
-            <!-- enum name="virtual_source" value="15"/ -->
-            <enum name="assistant" value="16"/>
-        </attr>
-
-        <!-- Icon resource ids to render on UI -->
-        <attr name="icon" />
-    </declare-styleable>
-</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
index 2c8f238..ed1cd81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
@@ -168,8 +168,9 @@
      *
      * @param reason a flag indicating which string should be shown, see
      *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
-     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
-     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
+     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
+     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
+     *               {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
      */
     public void showPromptReason(int reason) {
         mSecurityContainer.showPromptReason(reason);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index f8f3dc8..718bcf1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -137,6 +137,8 @@
                 return R.string.kg_prompt_reason_device_admin;
             case PROMPT_REASON_USER_REQUEST:
                 return R.string.kg_prompt_reason_user_request;
+            case PROMPT_REASON_PREPARE_FOR_UPDATE:
+                return R.string.kg_prompt_reason_prepare_for_update_password;
             case PROMPT_REASON_NONE:
                 return 0;
             default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 9eb168a..48c6bd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -438,6 +438,10 @@
             case PROMPT_REASON_USER_REQUEST:
                 mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request);
                 break;
+            case PROMPT_REASON_PREPARE_FOR_UPDATE:
+                mSecurityMessageDisplay.setMessage(
+                        R.string.kg_prompt_reason_prepare_for_update_pattern);
+                break;
             case PROMPT_REASON_NONE:
                 break;
             default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c67decc..6d865ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -116,6 +116,8 @@
                 return R.string.kg_prompt_reason_device_admin;
             case PROMPT_REASON_USER_REQUEST:
                 return R.string.kg_prompt_reason_user_request;
+            case PROMPT_REASON_PREPARE_FOR_UPDATE:
+                return R.string.kg_prompt_reason_prepare_for_update_pin;
             case PROMPT_REASON_NONE:
                 return 0;
             default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index e108194..09d4d5f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -51,6 +51,11 @@
      */
     int PROMPT_REASON_AFTER_LOCKOUT = 5;
 
+    /***
+     * Strong auth is require to prepare for an unattended update.
+     */
+    int PROMPT_REASON_PREPARE_FOR_UPDATE = 6;
+
     /**
      * Interface back to keyguard to tell it when security
      * @param callback
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index b960de5..35a65aa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -26,8 +26,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -37,7 +35,6 @@
 import android.view.WindowManager;
 import android.widget.ImageView;
 
-import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -258,12 +255,22 @@
 
         @Override
         public void run() {
-            try {
-                if (DEBUG) {
-                    Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
-                }
-                final int[] result = ITelephony.Stub.asInterface(ServiceManager
-                        .checkService("phone")).supplyPinReportResultForSubscriber(mSubId, mPin);
+            if (DEBUG) {
+                Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")");
+            }
+            TelephonyManager telephonyManager =
+                    ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                            .createForSubscriptionId(mSubId);
+            final int[] result = telephonyManager.supplyPinReportResult(mPin);
+            if (result == null || result.length == 0) {
+                Log.e(TAG, "Error result for supplyPinReportResult.");
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
+                    }
+                });
+            } else {
                 if (DEBUG) {
                     Log.v(TAG, "supplyPinReportResult returned: " + result[0] + " " + result[1]);
                 }
@@ -273,14 +280,6 @@
                         onSimCheckResponse(result[0], result[1]);
                     }
                 });
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException for supplyPinReportResult:", e);
-                post(new Runnable() {
-                    @Override
-                    public void run() {
-                        onSimCheckResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
-                    }
-                });
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 7e08ab3..dc68115 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -25,8 +25,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -36,7 +34,6 @@
 import android.view.WindowManager;
 import android.widget.ImageView;
 
-import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -318,10 +315,20 @@
 
         @Override
         public void run() {
-            try {
-                if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
-                final int[] result = ITelephony.Stub.asInterface(ServiceManager
-                    .checkService("phone")).supplyPukReportResultForSubscriber(mSubId, mPuk, mPin);
+            if (DEBUG) Log.v(TAG, "call supplyPukReportResult()");
+            TelephonyManager telephonyManager =
+                    ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                            .createForSubscriptionId(mSubId);
+            final int[] result = telephonyManager.supplyPukReportResult(mPuk, mPin);
+            if (result == null || result.length == 0) {
+                Log.e(TAG, "Error result for supplyPukReportResult.");
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
+                    }
+                });
+            } else {
                 if (DEBUG) {
                     Log.v(TAG, "supplyPukReportResult returned: " + result[0] + " " + result[1]);
                 }
@@ -331,14 +338,6 @@
                         onSimLockChangedResponse(result[0], result[1]);
                     }
                 });
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException for supplyPukReportResult:", e);
-                post(new Runnable() {
-                    @Override
-                    public void run() {
-                        onSimLockChangedResponse(PhoneConstants.PIN_GENERAL_FAILURE, -1);
-                    }
-                });
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 694c623..431862f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1072,7 +1072,7 @@
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
             } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
                 mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
-            } else if (TelephonyIntents.ACTION_SERVICE_STATE_CHANGED.equals(action)) {
+            } else if (Intent.ACTION_SERVICE_STATE.equals(action)) {
                 ServiceState serviceState = ServiceState.newFromBundle(intent.getExtras());
                 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -1633,8 +1633,8 @@
         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        filter.addAction(Intent.ACTION_SERVICE_STATE);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
         filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
         filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
         filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 4728327..02a4521 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -694,7 +694,7 @@
 
     public boolean isFalseGesture(MotionEvent ev) {
         boolean falsingDetected = mCallback.isAntiFalsingNeeded();
-        if (mFalsingManager.isClassiferEnabled()) {
+        if (mFalsingManager.isClassifierEnabled()) {
             falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
         } else {
             falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 995b35f..fdeaf1f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -134,7 +134,8 @@
                 if (isStack) {
                     mViewPositionOnTouchDown.set(mStack.getStackPosition());
                     mStack.onDragStart();
-                    if (!mStack.isShowingBubbleMenu() && !mStack.isExpanded()) {
+                    if (!mStack.isShowingBubbleMenu() && !mStack.isExpanded()
+                            && BubbleExperimentConfig.allowBubbleScreenshotMenu(mContext)) {
                         mShowBubbleMenuRunnable = mStack::showBubbleMenu;
                         mStack.postDelayed(mShowBubbleMenuRunnable,
                                 ViewConfiguration.getLongPressTimeout());
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index ea175ed..099909d 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -90,7 +90,7 @@
     }
 
     @Override
-    public boolean isClassiferEnabled() {
+    public boolean isClassifierEnabled() {
         return mIsClassiferEnabled;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index eb014ed..d6faed5 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -213,7 +213,7 @@
 
     private void onSessionStart() {
         if (FalsingLog.ENABLED) {
-            FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassiferEnabled());
+            FalsingLog.i("onSessionStart", "classifierEnabled=" + isClassifierEnabled());
             clearPendingWtf();
         }
         mBouncerOn = false;
@@ -246,7 +246,7 @@
         }
     }
 
-    public boolean isClassiferEnabled() {
+    public boolean isClassifierEnabled() {
         return mHumanInteractionClassifier.isEnabled();
     }
 
@@ -544,7 +544,7 @@
 
     public void dump(PrintWriter pw) {
         pw.println("FALSING MANAGER");
-        pw.print("classifierEnabled="); pw.println(isClassiferEnabled() ? 1 : 0);
+        pw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0);
         pw.print("mSessionActive="); pw.println(mSessionActive ? 1 : 0);
         pw.print("mBouncerOn="); pw.println(mSessionActive ? 1 : 0);
         pw.print("mState="); pw.println(StatusBarState.toShortString(mState));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index f475948..b2131e7 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -21,8 +21,8 @@
 import android.content.Context;
 import android.hardware.SensorManager;
 import android.net.Uri;
-import android.os.Handler;
 import android.provider.DeviceConfig;
+import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -32,6 +32,7 @@
 import com.android.systemui.classifier.brightline.FalsingDataProvider;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.FalsingPlugin;
 import com.android.systemui.plugins.PluginListener;
@@ -56,19 +57,22 @@
     private static final String PROXIMITY_SENSOR_TAG = "FalsingManager";
 
     private final ProximitySensor mProximitySensor;
+    private final DisplayMetrics mDisplayMetrics;
     private FalsingManager mInternalFalsingManager;
     private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener;
     private final DeviceConfigProxy mDeviceConfig;
     private boolean mBrightlineEnabled;
+    private final DockManager mDockManager;
     private Executor mUiBgExecutor;
 
     @Inject
-    FalsingManagerProxy(Context context, PluginManager pluginManager,
-            @Main Handler handler,
-            ProximitySensor proximitySensor,
-            DeviceConfigProxy deviceConfig,
+    FalsingManagerProxy(Context context, PluginManager pluginManager, @Main Executor executor,
+            DisplayMetrics displayMetrics, ProximitySensor proximitySensor,
+            DeviceConfigProxy deviceConfig, DockManager dockManager,
             @UiBackground Executor uiBgExecutor) {
+        mDisplayMetrics = displayMetrics;
         mProximitySensor = proximitySensor;
+        mDockManager = dockManager;
         mUiBgExecutor = uiBgExecutor;
         mProximitySensor.setTag(PROXIMITY_SENSOR_TAG);
         mProximitySensor.setSensorDelay(SensorManager.SENSOR_DELAY_GAME);
@@ -78,7 +82,7 @@
         setupFalsingManager(context);
         mDeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
-                handler::post,
+                executor,
                 mDeviceConfigListener
         );
 
@@ -125,10 +129,11 @@
             mInternalFalsingManager = new FalsingManagerImpl(context, mUiBgExecutor);
         } else {
             mInternalFalsingManager = new BrightLineFalsingManager(
-                    new FalsingDataProvider(context.getResources().getDisplayMetrics()),
+                    new FalsingDataProvider(mDisplayMetrics),
                     Dependency.get(KeyguardUpdateMonitor.class),
                     mProximitySensor,
-                    mDeviceConfig
+                    mDeviceConfig,
+                    mDockManager
             );
         }
     }
@@ -182,8 +187,8 @@
     }
 
     @Override
-    public boolean isClassiferEnabled() {
-        return mInternalFalsingManager.isClassiferEnabled();
+    public boolean isClassifierEnabled() {
+        return mInternalFalsingManager.isClassifierEnabled();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index bd0906a..b2e61a2 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -25,17 +25,21 @@
 import android.view.MotionEvent;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.classifier.Classifier;
+import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.sensors.ProximitySensor;
 
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Queue;
 
 /**
  * FalsingManager designed to make clear why a touch was rejected.
@@ -44,16 +48,20 @@
 
     static final boolean DEBUG = false;
     private static final String TAG = "FalsingManager";
+    private static final int RECENT_INFO_LOG_SIZE = 20;
 
     private final FalsingDataProvider mDataProvider;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final ProximitySensor mProximitySensor;
+    private final DockManager mDockManager;
     private boolean mSessionStarted;
     private MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
     private boolean mShowingAod;
     private boolean mScreenOn;
     private boolean mJustUnlockedWithFace;
+    private static final Queue<String> RECENT_INFO_LOG =
+            new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1);
 
     private final List<FalsingClassifier> mClassifiers;
 
@@ -71,14 +79,14 @@
                 }
             };
 
-    public BrightLineFalsingManager(
-            FalsingDataProvider falsingDataProvider,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            ProximitySensor proximitySensor,
-            DeviceConfigProxy deviceConfigProxy) {
+    public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
+            KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor,
+            DeviceConfigProxy deviceConfigProxy,
+            DockManager dockManager) {
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDataProvider = falsingDataProvider;
         mProximitySensor = proximitySensor;
+        mDockManager = dockManager;
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
 
         mMetricsLogger = new MetricsLogger();
@@ -134,29 +142,30 @@
     }
 
     @Override
-    public boolean isClassiferEnabled() {
+    public boolean isClassifierEnabled() {
         return true;
     }
 
     @Override
     public boolean isFalseTouch() {
-        boolean r = !mJustUnlockedWithFace && mClassifiers.stream().anyMatch(falsingClassifier -> {
-            boolean result = falsingClassifier.isFalseTouch();
-            if (result) {
-                logInfo(String.format(
-                        (Locale) null,
-                        "{classifier=%s, interactionType=%d}",
-                        falsingClassifier.getClass().getName(),
-                        mDataProvider.getInteractionType()));
-                String reason = falsingClassifier.getReason();
-                if (reason != null) {
-                    logInfo(reason);
-                }
-            } else {
-                logDebug(falsingClassifier.getClass().getName() + ": false");
-            }
-            return result;
-        });
+        boolean r = !mJustUnlockedWithFace && !mDockManager.isDocked()
+                && mClassifiers.stream().anyMatch(falsingClassifier -> {
+                    boolean result = falsingClassifier.isFalseTouch();
+                    if (result) {
+                        logInfo(String.format(
+                                (Locale) null,
+                                "{classifier=%s, interactionType=%d}",
+                                falsingClassifier.getClass().getName(),
+                                mDataProvider.getInteractionType()));
+                        String reason = falsingClassifier.getReason();
+                        if (reason != null) {
+                            logInfo(reason);
+                        }
+                    } else {
+                        logDebug(falsingClassifier.getClass().getName() + ": false");
+                    }
+                    return result;
+                });
 
         logDebug("Is false touch? " + r);
 
@@ -336,7 +345,18 @@
     }
 
     @Override
-    public void dump(PrintWriter printWriter) {
+    public void dump(PrintWriter pw) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println("BRIGHTLINE FALSING MANAGER");
+        ipw.print("classifierEnabled="); pw.println(isClassifierEnabled() ? 1 : 0);
+        ipw.print("mJustUnlockedWithFace="); pw.println(mJustUnlockedWithFace ? 1 : 0);
+        ipw.print("isDocked="); pw.println(mDockManager.isDocked() ? 1 : 0);
+        ipw.println();
+        ipw.println("Recent falsing info:");
+        ipw.increaseIndent();
+        for (String msg : RECENT_INFO_LOG) {
+            ipw.println(msg);
+        }
     }
 
     @Override
@@ -357,6 +377,10 @@
 
     static void logInfo(String msg) {
         Log.i(TAG, msg);
+        RECENT_INFO_LOG.add(msg);
+        while (RECENT_INFO_LOG.size() > RECENT_INFO_LOG_SIZE) {
+            RECENT_INFO_LOG.remove();
+        }
     }
 
     static void logError(String msg) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 0b73ab6..bb12b53 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -45,6 +45,7 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
@@ -72,6 +73,19 @@
 
     @Provides
     @Singleton
+    static ActivityManager provideActivityManager(Context context) {
+        return context.getSystemService(ActivityManager.class);
+    }
+
+
+    @Provides
+    @DisplayId
+    static int provideDisplayId(Context context) {
+        return context.getDisplayId();
+    }
+
+    @Provides
+    @Singleton
     static DevicePolicyManager provideDevicePolicyManager(Context context) {
         return context.getSystemService(DevicePolicyManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 442313d..58ddda9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,7 +35,7 @@
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.concurrency.ConcurrencyModule;
 import com.android.systemui.util.sensors.AsyncSensorManager;
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
similarity index 67%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
index 1fefda4..155a6d2 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
@@ -14,6 +14,17 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package com.android.systemui.dagger.qualifiers;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface DisplayId {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index 4b28540..657a308 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -206,7 +206,7 @@
             Log.d(TAG, "createEglSurface start");
         }
 
-        if (hasEglDisplay()) {
+        if (hasEglDisplay() && surfaceHolder.getSurface().isValid()) {
             int[] attrs = null;
             int wcgCapability = getWcgCapability();
             if (wcg && checkExtensionCapability(KHR_GL_COLOR_SPACE) && wcgCapability > 0) {
@@ -214,7 +214,8 @@
             }
             mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, attrs, 0);
         } else {
-            Log.w(TAG, "mEglDisplay is null");
+            Log.w(TAG, "Create EglSurface failed: hasEglDisplay=" + hasEglDisplay()
+                    + ", has valid surface=" + surfaceHolder.getSurface().isValid());
             return false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9fcf022..e13c3e0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -22,6 +22,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.systemui.DejankUtils.whitelistIpcs;
 
 import android.app.ActivityManager;
@@ -86,7 +87,7 @@
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
@@ -670,6 +671,8 @@
                 return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
             } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0) {
                 return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
+            } else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
+                return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
             }
             return KeyguardSecurityView.PROMPT_REASON_NONE;
         }
@@ -2102,7 +2105,7 @@
     }
 
     public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
-            ViewGroup container, NotificationPanelView panelView,
+            ViewGroup container, NotificationPanelViewController panelView,
             BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer,
             View notificationContainer, KeyguardBypassController bypassController) {
         mStatusBarKeyguardViewManagerLazy.get().registerStatusBar(statusBar, container, panelView,
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index eb19571..c8cf02a 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -585,10 +585,10 @@
                                 resolver,
                                 Global.LOW_POWER_MODE_TRIGGER_LEVEL,
                                 batterySaverTriggerLevel);
-                        Secure.putInt(
+                        Secure.putIntForUser(
                                 resolver,
                                 Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
-                                1);
+                                1, UserHandle.USER_CURRENT);
                     });
         } else {
             d.setTitle(R.string.battery_saver_confirmation_title);
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 9261412..bbff117 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -42,6 +42,7 @@
 
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Manages the lifecycle of a TileService.
@@ -84,7 +85,8 @@
     private int mBindTryCount;
     private int mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
     private boolean mBound;
-    boolean mReceiverRegistered;
+    private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
+    private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
     private boolean mUnbindImmediate;
     private TileChangeListener mChangeListener;
     // Return value from bindServiceAsUser, determines whether safe to call unbind.
@@ -274,7 +276,7 @@
 
     public void handleDestroy() {
         if (DEBUG) Log.d(TAG, "handleDestroy");
-        if (mReceiverRegistered) {
+        if (mPackageReceiverRegistered.get() || mUserReceiverRegistered.get()) {
             stopPackageListening();
         }
     }
@@ -310,17 +312,31 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
-        mContext.registerReceiverAsUser(this, mUser, filter, null, mHandler);
+        try {
+            mPackageReceiverRegistered.set(true);
+            mContext.registerReceiverAsUser(this, mUser, filter, null, mHandler);
+        } catch (Exception ex) {
+            mPackageReceiverRegistered.set(false);
+            Log.e(TAG, "Could not register package receiver", ex);
+        }
         filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
-        mBroadcastDispatcher.registerReceiver(this, filter, mHandler, mUser);
-        mReceiverRegistered = true;
+        try {
+            mUserReceiverRegistered.set(true);
+            mBroadcastDispatcher.registerReceiver(this, filter, mHandler, mUser);
+        } catch (Exception ex) {
+            mUserReceiverRegistered.set(false);
+            Log.e(TAG, "Could not register unlock receiver", ex);
+        }
     }
 
     private void stopPackageListening() {
         if (DEBUG) Log.d(TAG, "stopPackageListening");
-        mContext.unregisterReceiver(this);
-        mBroadcastDispatcher.unregisterReceiver(this);
-        mReceiverRegistered = false;
+        if (mUserReceiverRegistered.compareAndSet(true, false)) {
+            mBroadcastDispatcher.unregisterReceiver(this);
+        }
+        if (mPackageReceiverRegistered.compareAndSet(true, false)) {
+            mContext.unregisterReceiver(this);
+        }
     }
 
     public void setTileChangeListener(TileChangeListener changeListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 9a33c8c..f06c849 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -38,7 +38,6 @@
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.FlashlightTile;
 import com.android.systemui.qs.tiles.HotspotTile;
-import com.android.systemui.qs.tiles.IntentTile;
 import com.android.systemui.qs.tiles.LocationTile;
 import com.android.systemui.qs.tiles.NfcTile;
 import com.android.systemui.qs.tiles.NightDisplayTile;
@@ -182,8 +181,7 @@
                 return mUiModeNightTileProvider.get();
         }
 
-        // Intent tiles.
-        if (tileSpec.startsWith(IntentTile.PREFIX)) return IntentTile.create(mHost, tileSpec);
+        // Custom tiles
         if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
 
         // Debug tiles.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
deleted file mode 100644
index a639a95..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.tiles;
-
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.qs.QSTile.State;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.tileimpl.QSTileImpl;
-
-import java.util.Arrays;
-import java.util.Objects;
-
-public class IntentTile extends QSTileImpl<State> {
-    public static final String PREFIX = "intent(";
-
-    private PendingIntent mOnClick;
-    private String mOnClickUri;
-    private PendingIntent mOnLongClick;
-    private String mOnLongClickUri;
-    private int mCurrentUserId;
-    private String mIntentPackage;
-
-    private Intent mLastIntent;
-
-    private IntentTile(QSHost host, String action) {
-        super(host);
-        mContext.registerReceiver(mReceiver, new IntentFilter(action));
-    }
-
-    @Override
-    protected void handleDestroy() {
-        super.handleDestroy();
-        mContext.unregisterReceiver(mReceiver);
-    }
-
-    public static IntentTile create(QSHost host, String spec) {
-        if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
-            throw new IllegalArgumentException("Bad intent tile spec: " + spec);
-        }
-        final String action = spec.substring(PREFIX.length(), spec.length() - 1);
-        if (action.isEmpty()) {
-            throw new IllegalArgumentException("Empty intent tile spec action");
-        }
-        return new IntentTile(host, action);
-    }
-
-    @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
-    public State newTileState() {
-        return new State();
-    }
-
-    @Override
-    protected void handleUserSwitch(int newUserId) {
-        super.handleUserSwitch(newUserId);
-        mCurrentUserId = newUserId;
-    }
-
-    @Override
-    protected void handleClick() {
-        sendIntent("click", mOnClick, mOnClickUri);
-    }
-
-    @Override
-    public Intent getLongClickIntent() {
-        return null;
-    }
-
-    @Override
-    protected void handleLongClick() {
-        sendIntent("long-click", mOnLongClick, mOnLongClickUri);
-    }
-
-    private void sendIntent(String type, PendingIntent pi, String uri) {
-        try {
-            if (pi != null) {
-                if (pi.isActivity()) {
-                    Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(pi);
-                } else {
-                    pi.send();
-                }
-            } else if (uri != null) {
-                final Intent intent = Intent.parseUri(uri, Intent.URI_INTENT_SCHEME);
-                mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
-            }
-        } catch (Throwable t) {
-            Log.w(TAG, "Error sending " + type + " intent", t);
-        }
-    }
-
-    @Override
-    public CharSequence getTileLabel() {
-        return getState().label;
-    }
-
-    @Override
-    protected void handleUpdateState(State state, Object arg) {
-        Intent intent = (Intent) arg;
-        if (intent == null) {
-            if (mLastIntent == null) {
-                return;
-            }
-            // No intent but need to refresh state, just use the last one.
-            intent = mLastIntent;
-        }
-        // Save the last one in case we need it later.
-        mLastIntent = intent;
-        state.contentDescription = intent.getStringExtra("contentDescription");
-        state.label = intent.getStringExtra("label");
-        state.icon = null;
-        final byte[] iconBitmap = intent.getByteArrayExtra("iconBitmap");
-        if (iconBitmap != null) {
-            try {
-                state.icon = new BytesIcon(iconBitmap);
-            } catch (Throwable t) {
-                Log.w(TAG, "Error loading icon bitmap, length " + iconBitmap.length, t);
-            }
-        } else {
-            final int iconId = intent.getIntExtra("iconId", 0);
-            if (iconId != 0) {
-                final String iconPackage = intent.getStringExtra("iconPackage");
-                if (!TextUtils.isEmpty(iconPackage)) {
-                    state.icon = new PackageDrawableIcon(iconPackage, iconId);
-                } else {
-                    state.icon = ResourceIcon.get(iconId);
-                }
-            }
-        }
-        mOnClick = intent.getParcelableExtra("onClick");
-        mOnClickUri = intent.getStringExtra("onClickUri");
-        mOnLongClick = intent.getParcelableExtra("onLongClick");
-        mOnLongClickUri = intent.getStringExtra("onLongClickUri");
-        mIntentPackage = intent.getStringExtra("package");
-        mIntentPackage = mIntentPackage == null ? "" : mIntentPackage;
-    }
-
-    @Override
-    public int getMetricsCategory() {
-        return MetricsEvent.QS_INTENT;
-    }
-
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            refreshState(intent);
-        }
-    };
-
-    private static class BytesIcon extends Icon {
-        private final byte[] mBytes;
-
-        public BytesIcon(byte[] bytes) {
-            mBytes = bytes;
-        }
-
-        @Override
-        public Drawable getDrawable(Context context) {
-            final Bitmap b = BitmapFactory.decodeByteArray(mBytes, 0, mBytes.length);
-            return new BitmapDrawable(context.getResources(), b);
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            return o instanceof BytesIcon && Arrays.equals(((BytesIcon) o).mBytes, mBytes);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("BytesIcon[len=%s]", mBytes.length);
-        }
-    }
-
-    private class PackageDrawableIcon extends Icon {
-        private final String mPackage;
-        private final int mResId;
-
-        public PackageDrawableIcon(String pkg, int resId) {
-            mPackage = pkg;
-            mResId = resId;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (!(o instanceof PackageDrawableIcon)) return false;
-            final PackageDrawableIcon other = (PackageDrawableIcon) o;
-            return Objects.equals(other.mPackage, mPackage) && other.mResId == mResId;
-        }
-
-        @Override
-        public Drawable getDrawable(Context context) {
-            try {
-                return context.createPackageContext(mPackage, 0).getDrawable(mResId);
-            } catch (Throwable t) {
-                Log.w(TAG, "Error loading package drawable pkg=" + mPackage + " id=" + mResId, t);
-                return null;
-            }
-        }
-
-        @Override
-        public String toString() {
-            return String.format("PackageDrawableIcon[pkg=%s,id=0x%08x]", mPackage, mResId);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index d3ccbeb..77c3ad9 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.drawable.Icon;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.VirtualDisplay;
@@ -42,6 +41,7 @@
 import android.util.Log;
 import android.util.Size;
 import android.view.Surface;
+import android.view.WindowManager;
 import android.widget.Toast;
 
 import com.android.systemui.R;
@@ -76,7 +76,7 @@
     private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
 
     private static final int TOTAL_NUM_TRACKS = 1;
-    private static final int VIDEO_BIT_RATE = 6000000;
+    private static final int VIDEO_BIT_RATE = 10000000;
     private static final int VIDEO_FRAME_RATE = 30;
     private static final int AUDIO_BIT_RATE = 16;
     private static final int AUDIO_SAMPLE_RATE = 44100;
@@ -238,7 +238,9 @@
             mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
 
             // Set up video
-            DisplayMetrics metrics = getResources().getDisplayMetrics();
+            DisplayMetrics metrics = new DisplayMetrics();
+            WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+            wm.getDefaultDisplay().getRealMetrics(metrics);
             int screenWidth = metrics.widthPixels;
             int screenHeight = metrics.heightPixels;
             mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 525b5b7..12749fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -368,13 +368,14 @@
     public static class Builder {
         private final DisplayMetrics mDisplayMetrics;
         float mMaxLengthSeconds;
-        float mSpeedUpFactor = 0.0f;
-        float mX2 = -1.0f;
-        float mY2 = 1.0f;
+        float mSpeedUpFactor;
+        float mX2;
+        float mY2;
 
         @Inject
         public Builder(DisplayMetrics displayMetrics) {
             mDisplayMetrics = displayMetrics;
+            reset();
         }
 
         public Builder setMaxLengthSeconds(float maxLengthSeconds) {
@@ -397,6 +398,15 @@
             return this;
         }
 
+        public Builder reset() {
+            mMaxLengthSeconds = 0;
+            mSpeedUpFactor = 0.0f;
+            mX2 = -1.0f;
+            mY2 = 1.0f;
+
+            return this;
+        }
+
         public FlingAnimationUtils build() {
             return new FlingAnimationUtils(mDisplayMetrics, mMaxLengthSeconds, mSpeedUpFactor,
                     mX2, mY2);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 5abca6b..7ad07c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -168,7 +168,8 @@
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDockManager = dockManager;
-        mDockManager.addAlignmentStateListener(this::handleAlignStateChanged);
+        mDockManager.addAlignmentStateListener(
+                alignState -> mHandler.post(() -> handleAlignStateChanged(alignState)));
         // lock icon is not used on all form factors.
         if (mLockIcon != null) {
             mLockIcon.setOnLongClickListener(this::handleLockLongClick);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 8dd801b..8cc45f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -51,7 +51,7 @@
     private final Context mContext;
     private final NotificationManager mNotificationManager;
     private final Handler mMainHandler;
-    private final List<NotifServiceListener> mNotificationListeners = new ArrayList<>();
+    private final List<NotificationHandler> mNotificationHandlers = new ArrayList<>();
     private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
 
     @Inject
@@ -65,11 +65,11 @@
     }
 
     /** Registers a listener that's notified when notifications are added/removed/etc. */
-    public void addNotificationListener(NotifServiceListener listener) {
-        if (mNotificationListeners.contains(listener)) {
+    public void addNotificationHandler(NotificationHandler handler) {
+        if (mNotificationHandlers.contains(handler)) {
             throw new IllegalArgumentException("Listener is already added");
         }
-        mNotificationListeners.add(listener);
+        mNotificationHandlers.add(handler);
     }
 
     /** Registers a listener that's notified when any notification-related settings change. */
@@ -100,7 +100,7 @@
             final RankingMap completeMap = new RankingMap(newRankings.toArray(new Ranking[0]));
 
             for (StatusBarNotification sbn : notifications) {
-                for (NotifServiceListener listener : mNotificationListeners) {
+                for (NotificationHandler listener : mNotificationHandlers) {
                     listener.onNotificationPosted(sbn, completeMap);
                 }
             }
@@ -117,8 +117,8 @@
             mMainHandler.post(() -> {
                 processForRemoteInput(sbn.getNotification(), mContext);
 
-                for (NotifServiceListener listener : mNotificationListeners) {
-                    listener.onNotificationPosted(sbn, rankingMap);
+                for (NotificationHandler handler : mNotificationHandlers) {
+                    handler.onNotificationPosted(sbn, rankingMap);
                 }
             });
         }
@@ -130,8 +130,8 @@
         if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn + " reason: " + reason);
         if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
             mMainHandler.post(() -> {
-                for (NotifServiceListener listener : mNotificationListeners) {
-                    listener.onNotificationRemoved(sbn, rankingMap, reason);
+                for (NotificationHandler handler : mNotificationHandlers) {
+                    handler.onNotificationRemoved(sbn, rankingMap, reason);
                 }
             });
         }
@@ -148,8 +148,8 @@
         if (rankingMap != null) {
             RankingMap r = onPluginRankingUpdate(rankingMap);
             mMainHandler.post(() -> {
-                for (NotifServiceListener listener : mNotificationListeners) {
-                    listener.onNotificationRankingUpdate(r);
+                for (NotificationHandler handler : mNotificationHandlers) {
+                    handler.onNotificationRankingUpdate(r);
                 }
             });
         }
@@ -207,7 +207,7 @@
     }
 
     /** Interface for listening to add/remove events that we receive from NotificationManager. */
-    public interface NotifServiceListener {
+    public interface NotificationHandler {
         void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap);
         void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap);
         void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, int reason);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index 778443c..fdb793e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -33,6 +33,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Keeps track of the currently active {@link RemoteInputView}s.
@@ -108,8 +109,8 @@
      * @param token a token identifying the view that is managing the remote input
      */
     public void addRemoteInput(NotificationEntry entry, Object token) {
-        Preconditions.checkNotNull(entry);
-        Preconditions.checkNotNull(token);
+        Objects.requireNonNull(entry);
+        Objects.requireNonNull(token);
 
         boolean found = pruneWeakThenRemoveAndContains(
                 entry /* contains */, null /* remove */, token /* removeToken */);
@@ -129,7 +130,7 @@
      *              entry. If null, the entry is removed regardless.
      */
     public void removeRemoteInput(NotificationEntry entry, Object token) {
-        Preconditions.checkNotNull(entry);
+        Objects.requireNonNull(entry);
 
         pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
 
@@ -143,8 +144,8 @@
      * @param token the token of the view managing the remote input.
      */
     public void addSpinning(String key, Object token) {
-        Preconditions.checkNotNull(key);
-        Preconditions.checkNotNull(token);
+        Objects.requireNonNull(key);
+        Objects.requireNonNull(token);
 
         mSpinning.put(key, token);
     }
@@ -158,7 +159,7 @@
      *              entry. If null, the entry is removed regardless.
      */
     public void removeSpinning(String key, Object token) {
-        Preconditions.checkNotNull(key);
+        Objects.requireNonNull(key);
 
         if (token == null || mSpinning.get(key) == token) {
             mSpinning.remove(key);
@@ -237,7 +238,7 @@
 
 
     public void addCallback(Callback callback) {
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(callback);
         mCallbacks.add(callback);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 9b31234..6660569 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -37,7 +37,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
 
 /**
@@ -53,7 +53,7 @@
             CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
             - 16;
     private static final long LAUNCH_TIMEOUT = 500;
-    private final NotificationPanelView mNotificationPanel;
+    private final NotificationPanelViewController mNotificationPanel;
     private final NotificationListContainer mNotificationContainer;
     private final float mWindowCornerRadius;
     private final StatusBarWindowViewController mStatusBarWindowViewController;
@@ -69,7 +69,7 @@
     public ActivityLaunchAnimator(
             StatusBarWindowViewController statusBarWindowViewController,
             Callback callback,
-            NotificationPanelView notificationPanel,
+            NotificationPanelViewController notificationPanel,
             NotificationListContainer container) {
         mNotificationPanel = notificationPanel;
         mNotificationContainer = container;
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 b8afb78..33d97a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,7 +36,7 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationListener.NotifServiceListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationRemoveInterceptor;
@@ -179,7 +179,7 @@
 
     /** Once called, the NEM will start processing notification events from system server. */
     public void attach(NotificationListener notificationListener) {
-        notificationListener.addNotificationListener(mNotifListener);
+        notificationListener.addNotificationHandler(mNotifListener);
     }
 
     /** Adds a {@link NotificationEntryListener}. */
@@ -326,7 +326,7 @@
         }
     }
 
-    private final NotifServiceListener mNotifListener = new NotifServiceListener() {
+    private final NotificationHandler mNotifListener = new NotificationHandler() {
         @Override
         public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
             final boolean isUpdate = mActiveNotifications.containsKey(sbn.getKey());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 7b1dc07..f2765db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -16,14 +16,14 @@
 
 package com.android.systemui.statusbar.notification;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 
+import java.util.Objects;
+
 /**
  * Root controller for the list of notifications in the shade.
  *
@@ -39,9 +39,9 @@
             NotificationEntryManager entryManager,
             NotificationListContainer listContainer,
             DeviceProvisionedController deviceProvisionedController) {
-        mEntryManager = checkNotNull(entryManager);
-        mListContainer = checkNotNull(listContainer);
-        mDeviceProvisionedController = checkNotNull(deviceProvisionedController);
+        mEntryManager = Objects.requireNonNull(entryManager);
+        mListContainer = Objects.requireNonNull(listContainer);
+        mDeviceProvisionedController = Objects.requireNonNull(deviceProvisionedController);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
index cefb506..87aaea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/CollectionReadyForBuildListener.java
@@ -24,18 +24,6 @@
  */
 public interface CollectionReadyForBuildListener {
     /**
-     * Called after the NotifCollection has received an update from NotificationManager but before
-     * it dispatches any change events to its listeners. This is to inform the list builder that
-     * the first stage of the pipeline has been triggered. After events have been dispatched,
-     * onBuildList() will be called.
-     *
-     * While onBuildList() is always called after this method is called, the converse is not always
-     * true: sometimes the NotifCollection applies an update that does not need to dispatch events,
-     * in which case this method will be skipped and onBuildList will be called directly.
-     */
-    void onBeginDispatchToListeners();
-
-    /**
      * Called by the NotifCollection to indicate that something in the collection has changed and
      * that the list builder should regenerate the list.
      */
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 7f85c88..873cdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -35,8 +35,6 @@
 import static android.service.notification.NotificationListenerService.REASON_UNAUTOBUNDLED;
 import static android.service.notification.NotificationListenerService.REASON_USER_STOPPED;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
@@ -49,8 +47,9 @@
 import android.util.Log;
 
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationListener.NotifServiceListener;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CoalescedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
+import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer.BatchableNotificationHandler;
 import com.android.systemui.util.Assert;
 
 import java.lang.annotation.Retention;
@@ -60,6 +59,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -109,14 +109,14 @@
     }
 
     /** Initializes the NotifCollection and registers it to receive notification events. */
-    public void attach(NotificationListener listenerService) {
+    public void attach(GroupCoalescer groupCoalescer) {
         Assert.isMainThread();
         if (mAttached) {
             throw new RuntimeException("attach() called twice");
         }
         mAttached = true;
 
-        listenerService.addNotificationListener(mNotifServiceListener);
+        groupCoalescer.setNotificationHandler(mNotifHandler);
     }
 
     /**
@@ -170,7 +170,7 @@
             @CancellationReason int reason,
             @NonNull DismissedByUserStats stats) {
         Assert.isMainThread();
-        checkNotNull(stats);
+        Objects.requireNonNull(stats);
         checkForReentrantCall();
 
         removeNotification(entry.getKey(), null, reason, stats);
@@ -179,15 +179,52 @@
     private void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
         Assert.isMainThread();
 
+        postNotification(sbn, requireRanking(rankingMap, sbn.getKey()), rankingMap);
+        rebuildList();
+    }
+
+    private void onNotificationGroupPosted(List<CoalescedEvent> batch) {
+        Assert.isMainThread();
+
+        Log.d(TAG, "POSTED GROUP " + batch.get(0).getSbn().getGroupKey()
+                + " (" + batch.size() + " events)");
+        for (CoalescedEvent event : batch) {
+            postNotification(event.getSbn(), event.getRanking(), null);
+        }
+        rebuildList();
+    }
+
+    private void onNotificationRemoved(
+            StatusBarNotification sbn,
+            RankingMap rankingMap,
+            int reason) {
+        Assert.isMainThread();
+
+        Log.d(TAG, "REMOVED " + sbn.getKey() + " reason=" + reason);
+        removeNotification(sbn.getKey(), rankingMap, reason, null);
+    }
+
+    private void onNotificationRankingUpdate(RankingMap rankingMap) {
+        Assert.isMainThread();
+        applyRanking(rankingMap);
+        rebuildList();
+    }
+
+    private void postNotification(
+            StatusBarNotification sbn,
+            Ranking ranking,
+            @Nullable RankingMap rankingMap) {
         NotificationEntry entry = mNotificationSet.get(sbn.getKey());
 
         if (entry == null) {
             // A new notification!
             Log.d(TAG, "POSTED  " + sbn.getKey());
 
-            entry = new NotificationEntry(sbn, requireRanking(rankingMap, sbn.getKey()));
+            entry = new NotificationEntry(sbn, ranking);
             mNotificationSet.put(sbn.getKey(), entry);
-            applyRanking(rankingMap);
+            if (rankingMap != null) {
+                applyRanking(rankingMap);
+            }
 
             dispatchOnEntryAdded(entry);
 
@@ -200,34 +237,19 @@
             cancelLifetimeExtension(entry);
 
             entry.setSbn(sbn);
-            applyRanking(rankingMap);
+            if (rankingMap != null) {
+                applyRanking(rankingMap);
+            }
 
             dispatchOnEntryUpdated(entry);
         }
-
-        rebuildList();
-    }
-
-    private void onNotificationRemoved(
-            StatusBarNotification sbn,
-            @Nullable RankingMap rankingMap,
-            int reason) {
-        Assert.isMainThread();
-        Log.d(TAG, "REMOVED " + sbn.getKey() + " reason=" + reason);
-        removeNotification(sbn.getKey(), rankingMap, reason, null);
-    }
-
-    private void onNotificationRankingUpdate(RankingMap rankingMap) {
-        Assert.isMainThread();
-        applyRanking(rankingMap);
-        rebuildList();
     }
 
     private void removeNotification(
             String key,
             @Nullable RankingMap rankingMap,
             @CancellationReason int reason,
-            DismissedByUserStats dismissedByUserStats) {
+            @Nullable DismissedByUserStats dismissedByUserStats) {
 
         NotificationEntry entry = mNotificationSet.get(key);
         if (entry == null) {
@@ -272,7 +294,7 @@
         rebuildList();
     }
 
-    private void applyRanking(RankingMap rankingMap) {
+    private void applyRanking(@NonNull RankingMap rankingMap) {
         for (NotificationEntry entry : mNotificationSet.values()) {
             if (!isLifetimeExtended(entry)) {
                 Ranking ranking = requireRanking(rankingMap, entry.getKey());
@@ -339,9 +361,6 @@
 
     private void dispatchOnEntryAdded(NotificationEntry entry) {
         mAmDispatchingToOtherCode = true;
-        if (mBuildListener != null) {
-            mBuildListener.onBeginDispatchToListeners();
-        }
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onEntryAdded(entry);
         }
@@ -350,9 +369,6 @@
 
     private void dispatchOnEntryUpdated(NotificationEntry entry) {
         mAmDispatchingToOtherCode = true;
-        if (mBuildListener != null) {
-            mBuildListener.onBeginDispatchToListeners();
-        }
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onEntryUpdated(entry);
         }
@@ -364,22 +380,24 @@
             @CancellationReason int reason,
             boolean removedByUser) {
         mAmDispatchingToOtherCode = true;
-        if (mBuildListener != null) {
-            mBuildListener.onBeginDispatchToListeners();
-        }
         for (NotifCollectionListener listener : mNotifCollectionListeners) {
             listener.onEntryRemoved(entry, reason, removedByUser);
         }
         mAmDispatchingToOtherCode = false;
     }
 
-    private final NotifServiceListener mNotifServiceListener = new NotifServiceListener() {
+    private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
         @Override
         public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
             NotifCollection.this.onNotificationPosted(sbn, rankingMap);
         }
 
         @Override
+        public void onNotificationBatchPosted(List<CoalescedEvent> events) {
+            NotifCollection.this.onNotificationGroupPosted(events);
+        }
+
+        @Override
         public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
             NotifCollection.this.onNotificationRemoved(sbn, rankingMap, REASON_UNKNOWN);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
index f0a003f..19d90f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImpl.java
@@ -18,7 +18,6 @@
 
 import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
 import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpList;
-import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_BUILD_PENDING;
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_BUILD_STARTED;
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZING;
 import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_GROUPING;
@@ -198,12 +197,6 @@
     private final CollectionReadyForBuildListener mReadyForBuildListener =
             new CollectionReadyForBuildListener() {
                 @Override
-                public void onBeginDispatchToListeners() {
-                    Assert.isMainThread();
-                    mPipelineState.incrementTo(STATE_BUILD_PENDING);
-                }
-
-                @Override
                 public void onBuildList(Collection<NotificationEntry> entries) {
                     Assert.isMainThread();
                     mPipelineState.requireIsBefore(STATE_BUILD_STARTED);
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 dd3a3e0..4f4fb24 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
@@ -29,7 +29,6 @@
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING;
 
 import android.annotation.NonNull;
@@ -162,9 +161,9 @@
     public NotificationEntry(
             @NonNull StatusBarNotification sbn,
             @NonNull Ranking ranking) {
-        super(checkNotNull(checkNotNull(sbn).getKey()));
+        super(Objects.requireNonNull(Objects.requireNonNull(sbn).getKey()));
 
-        checkNotNull(ranking);
+        Objects.requireNonNull(ranking);
 
         mKey = sbn.getKey();
         setSbn(sbn);
@@ -194,8 +193,8 @@
      * TODO: Make this package-private
      */
     public void setSbn(@NonNull StatusBarNotification sbn) {
-        checkNotNull(sbn);
-        checkNotNull(sbn.getKey());
+        Objects.requireNonNull(sbn);
+        Objects.requireNonNull(sbn.getKey());
 
         if (!sbn.getKey().equals(mKey)) {
             throw new IllegalArgumentException("New key " + sbn.getKey()
@@ -223,8 +222,8 @@
      * TODO: Make this package-private
      */
     public void setRanking(@NonNull Ranking ranking) {
-        checkNotNull(ranking);
-        checkNotNull(ranking.getKey());
+        Objects.requireNonNull(ranking);
+        Objects.requireNonNull(ranking.getKey());
 
         if (!ranking.getKey().equals(mKey)) {
             throw new IllegalArgumentException("New key " + ranking.getKey()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
index 8afbc27..6c93618 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
 
@@ -51,6 +50,8 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
+import java.util.Objects;
+
 /** Handles inflating and updating views for notifications. */
 public class NotificationRowBinderImpl implements NotificationRowBinder {
 
@@ -265,7 +266,7 @@
         row.inflateViews();
 
         // bind the click event to the content area
-        checkNotNull(mNotificationClicker).register(row, sbn);
+        Objects.requireNonNull(mNotificationClicker).register(row, sbn);
     }
 
     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
index 5fc55da..5e0bd4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
@@ -24,6 +24,7 @@
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
 import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
+import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -36,6 +37,7 @@
  */
 @Singleton
 public class NewNotifPipeline implements Dumpable {
+    private final GroupCoalescer mGroupCoalescer;
     private final NotifCollection mNotifCollection;
     private final NotifListBuilderImpl mNotifPipeline;
     private final NotifCoordinators mNotifPluggableCoordinators;
@@ -45,10 +47,12 @@
 
     @Inject
     public NewNotifPipeline(
+            GroupCoalescer groupCoalescer,
             NotifCollection notifCollection,
             NotifListBuilderImpl notifPipeline,
             NotifCoordinators notifCoordinators,
             DumpController dumpController) {
+        mGroupCoalescer = groupCoalescer;
         mNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
         mNotifPluggableCoordinators = notifCoordinators;
@@ -58,20 +62,26 @@
     /** Hooks the new pipeline up to NotificationManager */
     public void initialize(
             NotificationListener notificationService) {
-        mFakePipelineConsumer.attach(mNotifPipeline);
-        mNotifPipeline.attach(mNotifCollection);
-        mNotifCollection.attach(notificationService);
-        mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
-
-        Log.d(TAG, "Notif pipeline initialized");
 
         mDumpController.registerDumpable("NotifPipeline", this);
+
+        // Wire up coordinators
+        mFakePipelineConsumer.attach(mNotifPipeline);
+        mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
+
+        // Wire up pipeline
+        mNotifPipeline.attach(mNotifCollection);
+        mNotifCollection.attach(mGroupCoalescer);
+        mGroupCoalescer.attach(notificationService);
+
+        Log.d(TAG, "Notif pipeline initialized");
     }
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mFakePipelineConsumer.dump(fd, pw, args);
         mNotifPluggableCoordinators.dump(fd, pw, args);
+        mGroupCoalescer.dump(fd, pw, args);
     }
 
     private static final String TAG = "NewNotifPipeline";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 85f828d..084d038 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -76,19 +76,17 @@
     }
 
     public static final int STATE_IDLE = 0;
-    public static final int STATE_BUILD_PENDING = 1;
-    public static final int STATE_BUILD_STARTED = 2;
-    public static final int STATE_RESETTING = 3;
-    public static final int STATE_PRE_GROUP_FILTERING = 4;
-    public static final int STATE_GROUPING = 5;
-    public static final int STATE_TRANSFORMING = 6;
-    public static final int STATE_SORTING = 7;
-    public static final int STATE_PRE_RENDER_FILTERING = 8;
-    public static final int STATE_FINALIZING = 9;
+    public static final int STATE_BUILD_STARTED = 1;
+    public static final int STATE_RESETTING = 2;
+    public static final int STATE_PRE_GROUP_FILTERING = 3;
+    public static final int STATE_GROUPING = 4;
+    public static final int STATE_TRANSFORMING = 5;
+    public static final int STATE_SORTING = 6;
+    public static final int STATE_PRE_RENDER_FILTERING = 7;
+    public static final int STATE_FINALIZING = 8;
 
     @IntDef(prefix = { "STATE_" }, value = {
             STATE_IDLE,
-            STATE_BUILD_PENDING,
             STATE_BUILD_STARTED,
             STATE_RESETTING,
             STATE_PRE_GROUP_FILTERING,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CoalescedEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CoalescedEvent.kt
new file mode 100644
index 0000000..b6218b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/CoalescedEvent.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.notifcollection
+
+import android.service.notification.NotificationListenerService.Ranking
+import android.service.notification.StatusBarNotification
+
+data class CoalescedEvent(
+    val key: String,
+    var position: Int,
+    var sbn: StatusBarNotification,
+    var ranking: Ranking,
+    var batch: EventBatch?
+) {
+    override fun toString(): String {
+        return "CoalescedEvent(key=$key)"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/EventBatch.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/EventBatch.java
new file mode 100644
index 0000000..ac51178
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/EventBatch.java
@@ -0,0 +1,42 @@
+/*
+ * 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.collection.notifcollection;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a set of notification post events for a particular notification group.
+ */
+public class EventBatch {
+    /** SystemClock.uptimeMillis() */
+    final long mCreatedTimestamp;
+
+    /** SBN.getGroupKey -- same for all members */
+    final String mGroupKey;
+
+    /**
+     * All members of the batch. Must share the same group key. Includes both children and
+     * summaries.
+     */
+    final List<CoalescedEvent> mMembers = new ArrayList<>();
+
+    EventBatch(long createdTimestamp, String groupKey) {
+        mCreatedTimestamp = createdTimestamp;
+        this.mGroupKey = groupKey;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
new file mode 100644
index 0000000..069c15f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescer.java
@@ -0,0 +1,303 @@
+/*
+ * 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.collection.notifcollection;
+
+import static com.android.systemui.statusbar.notification.logging.NotifEvent.COALESCED_EVENT;
+import static com.android.systemui.statusbar.notification.logging.NotifEvent.EARLY_BATCH_EMIT;
+import static com.android.systemui.statusbar.notification.logging.NotifEvent.EMIT_EVENT_BATCH;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.MainThread;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+/**
+ * An attempt to make posting notification groups an atomic process
+ *
+ * Due to the nature of the groups API, individual members of a group are posted to system server
+ * one at a time. This means that whenever a group member is posted, we don't know if there are any
+ * more members soon to be posted.
+ *
+ * The Coalescer sits between the NotificationListenerService and the NotifCollection. It clusters
+ * new notifications that are members of groups and delays their posting until any of the following
+ * criteria are met:
+ *
+ * - A few milliseconds pass (see groupLingerDuration on the constructor)
+ * - Any notification in the delayed group is updated
+ * - Any notification in the delayed group is retracted
+ *
+ * Once we cross this threshold, all members of the group in question are posted atomically to the
+ * NotifCollection. If this process was triggered by an update or removal, then that event is then
+ * passed along to the NotifCollection.
+ */
+@MainThread
+public class GroupCoalescer implements Dumpable {
+    private final DelayableExecutor mMainExecutor;
+    private final SystemClock mClock;
+    private final NotifLog mLog;
+    private final long mGroupLingerDuration;
+
+    private BatchableNotificationHandler mHandler;
+
+    private final Map<String, CoalescedEvent> mCoalescedEvents = new ArrayMap<>();
+    private final Map<String, EventBatch> mBatches = new ArrayMap<>();
+
+    @Inject
+    public GroupCoalescer(
+            @Main DelayableExecutor mainExecutor,
+            SystemClock clock, NotifLog log) {
+        this(mainExecutor, clock, log, GROUP_LINGER_DURATION);
+    }
+
+    /**
+     * @param groupLingerDuration How long, in ms, that notifications that are members of a group
+     *                            are delayed within the GroupCoalescer before being posted
+     */
+    GroupCoalescer(
+            @Main DelayableExecutor mainExecutor,
+            SystemClock clock,
+            NotifLog log,
+            long groupLingerDuration) {
+        mMainExecutor = mainExecutor;
+        mClock = clock;
+        mLog = log;
+        mGroupLingerDuration = groupLingerDuration;
+    }
+
+    /**
+     * Attaches the coalescer to the pipeline, making it ready to receive events. Should only be
+     * called once.
+     */
+    public void attach(NotificationListener listenerService) {
+        listenerService.addNotificationHandler(mListener);
+    }
+
+    public void setNotificationHandler(BatchableNotificationHandler handler) {
+        mHandler = handler;
+    }
+
+    private final NotificationHandler mListener = new NotificationHandler() {
+        @Override
+        public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+            maybeEmitBatch(sbn.getKey());
+            applyRanking(rankingMap);
+
+            final boolean shouldCoalesce = handleNotificationPosted(sbn, rankingMap);
+
+            if (shouldCoalesce) {
+                mLog.log(COALESCED_EVENT, String.format("Coalesced notification %s", sbn.getKey()));
+                mHandler.onNotificationRankingUpdate(rankingMap);
+            } else {
+                mHandler.onNotificationPosted(sbn, rankingMap);
+            }
+        }
+
+        @Override
+        public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+            maybeEmitBatch(sbn.getKey());
+            applyRanking(rankingMap);
+            mHandler.onNotificationRemoved(sbn, rankingMap);
+        }
+
+        @Override
+        public void onNotificationRemoved(
+                StatusBarNotification sbn,
+                RankingMap rankingMap,
+                int reason) {
+            maybeEmitBatch(sbn.getKey());
+            applyRanking(rankingMap);
+            mHandler.onNotificationRemoved(sbn, rankingMap, reason);
+        }
+
+        @Override
+        public void onNotificationRankingUpdate(RankingMap rankingMap) {
+            applyRanking(rankingMap);
+            mHandler.onNotificationRankingUpdate(rankingMap);
+        }
+    };
+
+    private void maybeEmitBatch(String memberKey) {
+        CoalescedEvent event = mCoalescedEvents.get(memberKey);
+        if (event != null) {
+            mLog.log(EARLY_BATCH_EMIT,
+                    String.format("Modification of %s triggered early emit of batched group %s",
+                            memberKey, requireNonNull(event.getBatch()).mGroupKey));
+            emitBatch(requireNonNull(event.getBatch()));
+        }
+    }
+
+    /**
+     * @return True if the notification was coalesced and false otherwise.
+     */
+    private boolean handleNotificationPosted(
+            StatusBarNotification sbn,
+            RankingMap rankingMap) {
+
+        if (mCoalescedEvents.containsKey(sbn.getKey())) {
+            throw new IllegalStateException(
+                    "Notification has already been coalesced: " + sbn.getKey());
+        }
+
+        if (sbn.isGroup()) {
+            EventBatch batch = startBatchingGroup(sbn.getGroupKey());
+            CoalescedEvent event =
+                    new CoalescedEvent(
+                            sbn.getKey(),
+                            batch.mMembers.size(),
+                            sbn,
+                            requireRanking(rankingMap, sbn.getKey()),
+                            batch);
+
+            batch.mMembers.add(event);
+
+            mCoalescedEvents.put(event.getKey(), event);
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private EventBatch startBatchingGroup(final String groupKey) {
+        EventBatch batch = mBatches.get(groupKey);
+        if (batch == null) {
+            final EventBatch newBatch = new EventBatch(mClock.uptimeMillis(), groupKey);
+            mBatches.put(groupKey, newBatch);
+            mMainExecutor.executeDelayed(() -> emitBatch(newBatch), mGroupLingerDuration);
+
+            batch = newBatch;
+        }
+        return batch;
+    }
+
+    private void emitBatch(EventBatch batch) {
+        if (batch != mBatches.get(batch.mGroupKey)) {
+            // If we emit a batch early, we don't want to emit it a second time when its timeout
+            // expires.
+            return;
+        }
+        if (batch.mMembers.isEmpty()) {
+            throw new IllegalStateException("Batch " + batch.mGroupKey + " cannot be empty");
+        }
+
+        mBatches.remove(batch.mGroupKey);
+
+        final List<CoalescedEvent> events = new ArrayList<>(batch.mMembers);
+        for (CoalescedEvent event : events) {
+            mCoalescedEvents.remove(event.getKey());
+            event.setBatch(null);
+        }
+        events.sort(mEventComparator);
+
+        mLog.log(EMIT_EVENT_BATCH, "Emitting event batch for group " + batch.mGroupKey);
+
+        mHandler.onNotificationBatchPosted(events);
+    }
+
+    private Ranking requireRanking(RankingMap rankingMap, String key) {
+        Ranking ranking = new Ranking();
+        if (!rankingMap.getRanking(key, ranking)) {
+            throw new IllegalArgumentException("Ranking map does not contain key " + key);
+        }
+        return ranking;
+    }
+
+    private void applyRanking(RankingMap rankingMap) {
+        for (CoalescedEvent event : mCoalescedEvents.values()) {
+            Ranking ranking = new Ranking();
+            if (!rankingMap.getRanking(event.getKey(), ranking)) {
+                throw new IllegalStateException(
+                        "Ranking map doesn't contain key: " + event.getKey());
+            }
+            event.setRanking(ranking);
+        }
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        long now = mClock.uptimeMillis();
+
+        int eventCount = 0;
+
+        pw.println();
+        pw.println("Coalesced notifications:");
+        for (EventBatch batch : mBatches.values()) {
+            pw.println("   Batch " + batch.mGroupKey + ":");
+            pw.println("       Created" + (now - batch.mCreatedTimestamp) + "ms ago");
+            for (CoalescedEvent event : batch.mMembers) {
+                pw.println("       " + event.getKey());
+                eventCount++;
+            }
+        }
+
+        if (eventCount != mCoalescedEvents.size()) {
+            pw.println("    ERROR: batches contain " + mCoalescedEvents.size() + " events but"
+                    + " am tracking " + mCoalescedEvents.size() + " total events");
+            pw.println("    All tracked events:");
+            for (CoalescedEvent event : mCoalescedEvents.values()) {
+                pw.println("        " + event.getKey());
+            }
+        }
+    }
+
+    private final Comparator<CoalescedEvent> mEventComparator = (o1, o2) -> {
+        int cmp = Boolean.compare(
+                o2.getSbn().getNotification().isGroupSummary(),
+                o1.getSbn().getNotification().isGroupSummary());
+        if (cmp == 0) {
+            cmp = o1.getPosition() - o2.getPosition();
+        }
+        return cmp;
+    };
+
+    /**
+     * Extension of {@link NotificationListener.NotificationHandler} to include notification
+     * groups.
+     */
+    public interface BatchableNotificationHandler extends NotificationHandler {
+        /**
+         * Fired whenever the coalescer needs to emit a batch of multiple post events. This is
+         * usually the addition of a new group, but can contain just a single event, or just an
+         * update to a subset of an existing group.
+         */
+        void onNotificationBatchPosted(List<CoalescedEvent> events);
+    }
+
+    private static final int GROUP_LINGER_DURATION = 40;
+}
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
index c18af80..c6c36ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java
@@ -23,6 +23,7 @@
 import com.android.systemui.log.RichEvent;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -58,7 +59,10 @@
      */
     @Override
     public String[] getEventLabels() {
-        assert (TOTAL_EVENT_LABELS == (TOTAL_NEM_EVENT_TYPES + TOTAL_LIST_BUILDER_EVENT_TYPES));
+        assert (TOTAL_EVENT_LABELS
+                == (TOTAL_NEM_EVENT_TYPES
+                        + TOTAL_LIST_BUILDER_EVENT_TYPES
+                        + TOTAL_COALESCER_EVENT_TYPES));
         return EVENT_LABELS;
     }
 
@@ -108,7 +112,12 @@
             LIFETIME_EXTENDED,
             REMOVE_INTERCEPTED,
             INFLATION_ABORTED,
-            INFLATED
+            INFLATED,
+
+            // GroupCoalescer
+            COALESCED_EVENT,
+            EARLY_BATCH_EMIT,
+            EMIT_EVENT_BATCH
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
@@ -141,7 +150,12 @@
                     "LifetimeExtended",
                     "RemoveIntercepted",
                     "InflationAborted",
-                    "Inflated"
+                    "Inflated",
+
+                    // GroupCoalescer labels:
+                    "CoalescedEvent",
+                    "EarlyBatchEmit",
+                    "EmitEventBatch"
             };
 
     private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length;
@@ -167,17 +181,28 @@
     /**
      * Events related to {@link NotificationEntryManager}
      */
-    public static final int NOTIF_ADDED = TOTAL_LIST_BUILDER_EVENT_TYPES + 0;
-    public static final int NOTIF_REMOVED = TOTAL_LIST_BUILDER_EVENT_TYPES + 1;
-    public static final int NOTIF_UPDATED = TOTAL_LIST_BUILDER_EVENT_TYPES + 2;
-    public static final int FILTER = TOTAL_LIST_BUILDER_EVENT_TYPES + 3;
-    public static final int SORT = TOTAL_LIST_BUILDER_EVENT_TYPES + 4;
-    public static final int FILTER_AND_SORT = TOTAL_LIST_BUILDER_EVENT_TYPES + 5;
-    public static final int NOTIF_VISIBILITY_CHANGED = TOTAL_LIST_BUILDER_EVENT_TYPES + 6;
-    public static final int LIFETIME_EXTENDED = TOTAL_LIST_BUILDER_EVENT_TYPES + 7;
+    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 = TOTAL_LIST_BUILDER_EVENT_TYPES + 8;
-    public static final int INFLATION_ABORTED = TOTAL_LIST_BUILDER_EVENT_TYPES + 9;
-    public static final int INFLATED = TOTAL_LIST_BUILDER_EVENT_TYPES + 10;
+    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;
+    private static final int TOTAL_COALESCER_EVENT_TYPES = 3;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 71342c5..823dd5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -134,7 +134,7 @@
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -496,8 +496,7 @@
     protected boolean mClearAllEnabled;
 
     private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
-    private NotificationPanelView mNotificationPanel;
-    private final ShadeController mShadeController = Dependency.get(ShadeController.class);
+    private NotificationPanelViewController mNotificationPanelController;
 
     private final NotificationGutsManager mNotificationGutsManager;
     private final NotificationSectionsManager mSectionsManager;
@@ -5519,7 +5518,8 @@
 
         if (viewsToRemove.isEmpty()) {
             if (closeShade) {
-                mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                Dependency.get(ShadeController.class).animateCollapsePanels(
+                        CommandQueue.FLAG_EXCLUDE_NONE);
             }
             return;
         }
@@ -5577,11 +5577,12 @@
 
         final Runnable onSlideAwayAnimationComplete = () -> {
             if (closeShade) {
-                mShadeController.addPostCollapseAction(() -> {
+                Dependency.get(ShadeController.class).addPostCollapseAction(() -> {
                     setDismissAllInProgress(false);
                     onAnimationComplete.run();
                 });
-                mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
+                Dependency.get(ShadeController.class).animateCollapsePanels(
+                        CommandQueue.FLAG_EXCLUDE_NONE);
             } else {
                 setDismissAllInProgress(false);
                 onAnimationComplete.run();
@@ -5657,8 +5658,9 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setNotificationPanel(NotificationPanelView notificationPanelView) {
-        mNotificationPanel = notificationPanelView;
+    public void setNotificationPanelController(
+            NotificationPanelViewController notificationPanelViewController) {
+        mNotificationPanelController = notificationPanelViewController;
     }
 
     public void updateIconAreaViews() {
@@ -6402,7 +6404,7 @@
 
                 if (!mAmbientState.isDozing() || startingChild != null) {
                     // We have notifications, go to locked shade.
-                    mShadeController.goToLockedShade(startingChild);
+                    Dependency.get(ShadeController.class).goToLockedShade(startingChild);
                     if (startingChild instanceof ExpandableNotificationRow) {
                         ExpandableNotificationRow row = (ExpandableNotificationRow) startingChild;
                         row.onExpandedByGesture(true /* drag down is always an open */);
@@ -6441,7 +6443,7 @@
 
         @Override
         public void setEmptyDragAmount(float amount) {
-            mNotificationPanel.setEmptyDragAmount(amount);
+            mNotificationPanelController.setEmptyDragAmount(amount);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index b444fa5..add982d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.RectF;
@@ -32,6 +30,8 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 
+import java.util.Objects;
+
 /**
  * Similar in size and appearance to the NotificationShelf, appears at the beginning of some
  * notification sections. Currently only used for gentle notifications.
@@ -51,13 +51,13 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mContents = checkNotNull(findViewById(R.id.content));
+        mContents = Objects.requireNonNull(findViewById(R.id.content));
         bindContents();
     }
 
     private void bindContents() {
-        mLabelView = checkNotNull(findViewById(R.id.header_label));
-        mClearAllButton = checkNotNull(findViewById(R.id.btn_clear_all));
+        mLabelView = Objects.requireNonNull(findViewById(R.id.header_label));
+        mClearAllButton = Objects.requireNonNull(findViewById(R.id.btn_clear_all));
         if (mOnClearClickListener != null) {
             mClearAllButton.setOnClickListener(mOnClearClickListener);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 8b31da4..e03db2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -230,7 +230,7 @@
         // The shelf will be hidden when dozing with a custom clock, we must show notification
         // icons in this occasion.
         if (mStatusBarStateController.isDozing()
-                && mStatusBarComponent.getPanel().hasCustomClock()) {
+                && mStatusBarComponent.getPanelController().hasCustomClock()) {
             state |= DISABLE_CLOCK | DISABLE_SYSTEM_INFO;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 09ebb64..accd2a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -70,7 +70,6 @@
     boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
             "persist.sysui.wake_performs_auth", true);
     private boolean mDozingRequested;
-    private boolean mDozing;
     private boolean mPulsing;
     private WakefulnessLifecycle mWakefulnessLifecycle;
     private final SysuiStatusBarStateController mStatusBarStateController;
@@ -92,7 +91,7 @@
     private final LockscreenLockIconController mLockscreenLockIconController;
     private NotificationIconAreaController mNotificationIconAreaController;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private NotificationPanelView mNotificationPanel;
+    private NotificationPanelViewController mNotificationPanel;
     private View mAmbientIndicationContainer;
     private StatusBar mStatusBar;
 
@@ -142,7 +141,7 @@
             NotificationIconAreaController notificationIconAreaController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             StatusBarWindowViewController statusBarWindowViewController,
-            NotificationPanelView notificationPanel, View ambientIndicationContainer) {
+            NotificationPanelViewController notificationPanel, View ambientIndicationContainer) {
         mStatusBar = statusBar;
         mNotificationIconAreaController = notificationIconAreaController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
@@ -196,8 +195,8 @@
     public void startDozing() {
         if (!mDozingRequested) {
             mDozingRequested = true;
-            mDozeLog.traceDozing(mDozing);
             updateDozing();
+            mDozeLog.traceDozing(mStatusBarStateController.isDozing());
             mStatusBar.updateIsKeyguard();
         }
     }
@@ -281,8 +280,8 @@
     public void stopDozing() {
         if (mDozingRequested) {
             mDozingRequested = false;
-            mDozeLog.traceDozing(mDozing);
             updateDozing();
+            mDozeLog.traceDozing(mStatusBarStateController.isDozing());
         }
     }
 
@@ -292,7 +291,7 @@
             mDozeLog.tracePulseTouchDisabledByProx(ignore);
         }
         mIgnoreTouchWhilePulsing = ignore;
-        if (mDozing && ignore) {
+        if (mStatusBarStateController.isDozing() && ignore) {
             mStatusBarWindowViewController.cancelCurrentTouch();
         }
     }
@@ -448,10 +447,6 @@
         return mAnimateScreenOff;
     }
 
-    public void setDozing(boolean dozing) {
-        mDozing = dozing;
-    }
-
     boolean getIgnoreTouchWhilePulsing() {
         return mIgnoreTouchWhilePulsing;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index 8e5a912..7b20a7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -58,7 +58,7 @@
     private final View mClockView;
     private final View mOperatorNameView;
     private final DarkIconDispatcher mDarkIconDispatcher;
-    private final NotificationPanelView mPanelView;
+    private final NotificationPanelViewController mNotificationPanelViewController;
     private final Consumer<ExpandableNotificationRow>
             mSetTrackingHeadsUp = this::setTrackingHeadsUp;
     private final Runnable mUpdatePanelTranslation = this::updatePanelTranslation;
@@ -96,13 +96,14 @@
             SysuiStatusBarStateController statusBarStateController,
             KeyguardBypassController keyguardBypassController,
             KeyguardStateController keyguardStateController,
-            NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue) {
+            NotificationWakeUpCoordinator wakeUpCoordinator, CommandQueue commandQueue,
+            NotificationPanelViewController notificationPanelViewController) {
         this(notificationIconAreaController, headsUpManager, statusBarStateController,
                 keyguardBypassController, wakeUpCoordinator, keyguardStateController,
                 commandQueue,
                 statusbarView.findViewById(R.id.heads_up_status_bar_view),
                 statusbarView.findViewById(R.id.notification_stack_scroller),
-                statusbarView.findViewById(R.id.notification_panel),
+                notificationPanelViewController,
                 statusbarView.findViewById(R.id.clock),
                 statusbarView.findViewById(R.id.operator_name_frame),
                 statusbarView.findViewById(R.id.centered_icon_area));
@@ -119,7 +120,7 @@
             CommandQueue commandQueue,
             HeadsUpStatusBarView headsUpStatusBarView,
             NotificationStackScrollLayout stackScroller,
-            NotificationPanelView panelView,
+            NotificationPanelViewController notificationPanelViewController,
             View clockView,
             View operatorNameView,
             View centeredIconView) {
@@ -131,10 +132,10 @@
         headsUpStatusBarView.setOnDrawingRectChangedListener(
                 () -> updateIsolatedIconLocation(true /* requireUpdate */));
         mStackScroller = stackScroller;
-        mPanelView = panelView;
-        panelView.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
-        panelView.addVerticalTranslationListener(mUpdatePanelTranslation);
-        panelView.setHeadsUpAppearanceController(this);
+        mNotificationPanelViewController = notificationPanelViewController;
+        notificationPanelViewController.addTrackingHeadsUpListener(mSetTrackingHeadsUp);
+        notificationPanelViewController.addVerticalTranslationListener(mUpdatePanelTranslation);
+        notificationPanelViewController.setHeadsUpAppearanceController(this);
         mStackScroller.addOnExpandedHeightChangedListener(mSetExpandedHeight);
         mStackScroller.addOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mStackScroller.setHeadsUpAppearanceController(this);
@@ -169,9 +170,9 @@
         mHeadsUpManager.removeListener(this);
         mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null);
         mWakeUpCoordinator.removeListener(this);
-        mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
-        mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation);
-        mPanelView.setHeadsUpAppearanceController(null);
+        mNotificationPanelViewController.removeTrackingHeadsUpListener(mSetTrackingHeadsUp);
+        mNotificationPanelViewController.removeVerticalTranslationListener(mUpdatePanelTranslation);
+        mNotificationPanelViewController.setHeadsUpAppearanceController(null);
         mStackScroller.removeOnExpandedHeightChangedListener(mSetExpandedHeight);
         mStackScroller.removeOnLayoutChangeListener(mStackScrollLayoutChangeListener);
         mDarkIconDispatcher.removeDarkReceiver(this);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index ac06d9d..c282cb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -39,12 +39,12 @@
     private boolean mTouchingHeadsUpView;
     private boolean mTrackingHeadsUp;
     private boolean mCollapseSnoozes;
-    private NotificationPanelView mPanel;
+    private NotificationPanelViewController mPanel;
     private ExpandableNotificationRow mPickedChild;
 
     public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
             Callback callback,
-            NotificationPanelView notificationPanelView) {
+            NotificationPanelViewController notificationPanelView) {
         mHeadsUpManager = headsUpManager;
         mCallback = callback;
         mPanel = notificationPanelView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index d95d2b7..d3e44ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -155,7 +155,6 @@
     };
 
     private boolean mLeftIsVoiceAssist;
-    private AssistManager mAssistManager;
     private Drawable mLeftAssistIcon;
 
     private IntentButton mRightButton = new DefaultRightButton();
@@ -254,7 +253,6 @@
         mActivityStarter = Dependency.get(ActivityStarter.class);
         mFlashlightController = Dependency.get(FlashlightController.class);
         mAccessibilityController = Dependency.get(AccessibilityController.class);
-        mAssistManager = Dependency.get(AssistManager.class);
         mActivityIntentHelper = new ActivityIntentHelper(getContext());
         updateLeftAffordance();
     }
@@ -551,7 +549,7 @@
         Runnable runnable = new Runnable() {
             @Override
             public void run() {
-                mAssistManager.launchVoiceAssistFromKeyguard();
+                Dependency.get(AssistManager.class).launchVoiceAssistFromKeyguard();
             }
         };
         if (!mKeyguardStateController.canDismissLockScreen()) {
@@ -565,7 +563,7 @@
     }
 
     private boolean canLaunchVoiceAssist() {
-        return mAssistManager.canVoiceAssistBeLaunchedFromKeyguard();
+        return Dependency.get(AssistManager.class).canVoiceAssistBeLaunchedFromKeyguard();
     }
 
     private void launchPhone() {
@@ -647,7 +645,7 @@
         }
         if (mLeftIsVoiceAssist) {
             mLeftPreview = mPreviewInflater.inflatePreviewFromService(
-                    mAssistManager.getVoiceInteractorComponentName());
+                    Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
         } else {
             mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 4e91e4c..a3f14ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -90,7 +90,7 @@
     private int mContainerTopPadding;
 
     /**
-     * @see NotificationPanelView#getExpandedFraction()
+     * @see NotificationPanelViewController#getExpandedFraction()
      */
     private float mPanelExpansion;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index d4cf272..a3b1b5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -350,7 +350,7 @@
             mIsOnDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
         }
 
-        mNavigationBarView.setComponents(mStatusBarLazy.get().getPanel(), mAssistManager);
+        mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
         mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5a1b20d..ba9ba6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -67,7 +67,6 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistHandleViewController;
-import com.android.systemui.assist.AssistManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
@@ -148,7 +147,7 @@
 
     private NavigationBarInflaterView mNavigationInflaterView;
     private RecentsOnboarding mRecentsOnboarding;
-    private NotificationPanelView mPanelView;
+    private NotificationPanelViewController mPanelView;
     private FloatingRotationButton mFloatingRotationButton;
     private RotationButtonController mRotationButtonController;
 
@@ -349,7 +348,7 @@
         return mBarTransitions.getLightTransitionsController();
     }
 
-    public void setComponents(NotificationPanelView panel, AssistManager assistManager) {
+    public void setComponents(NotificationPanelViewController panel) {
         mPanelView = panel;
         updatePanelSystemUiStateFlags();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 199d52f..9dc8fbf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -16,118 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.app.ActivityManager;
-import android.app.Fragment;
-import android.app.StatusBarManager;
 import android.content.Context;
-import android.content.pm.ResolveInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.PowerManager;
-import android.os.SystemClock;
-import android.provider.DeviceConfig;
-import android.provider.Settings;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowInsets;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.FrameLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.keyguard.KeyguardClockSwitch;
-import com.android.keyguard.KeyguardStatusView;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.HomeControlsPlugin;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.KeyguardAffordanceView;
-import com.android.systemui.statusbar.KeyguardIndicationController;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
-import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.util.InjectionInflationController;
-import com.android.systemui.util.Utils;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-public class NotificationPanelView extends PanelView implements
-        ExpandableView.OnHeightChangedListener,
-        View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
-        KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener,
-        OnHeadsUpChangedListener, QS.HeightListener, ZenModeController.Callback,
-        ConfigurationController.ConfigurationListener, StateListener,
-        PulseExpansionHandler.ExpansionCallback, DynamicPrivacyController.Listener,
-        NotificationWakeUpCoordinator.WakeUpListener {
+public class NotificationPanelView extends PanelView {
 
     private static final boolean DEBUG = false;
 
@@ -136,2542 +35,26 @@
      */
     public static final int FLING_EXPAND = 0;
 
-    /**
-     * Fling collapsing QS, potentially stopping when QS becomes QQS.
-     */
-    public static final int FLING_COLLAPSE = 1;
-
-    /**
-     * Fling until QS is completely hidden.
-     */
-    public static final int FLING_HIDE = 2;
-    private final DozeParameters mDozeParameters;
-
-    private double mQqsSplitFraction;
-
-    // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
-    // changed.
-    private static final int CAP_HEIGHT = 1456;
-    private static final int FONT_HEIGHT = 2163;
-
-    /**
-     * Maximum time before which we will expand the panel even for slow motions when getting a
-     * touch passed over from launcher.
-     */
-    private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
-
     static final String COUNTER_PANEL_OPEN = "panel_open";
     static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
-    private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
-
-    private static final Rect mDummyDirtyRect = new Rect(0, 0, 1, 1);
-    private static final Rect mEmptyRect = new Rect();
-
-    private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = new AnimationProperties()
-            .setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-    private static final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT
-            = AnimatableProperty.from("KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
-            NotificationPanelView::setKeyguardHeadsUpShowingAmount,
-            NotificationPanelView::getKeyguardHeadsUpShowingAmount,
-            R.id.keyguard_hun_animator_tag,
-            R.id.keyguard_hun_animator_end_tag,
-            R.id.keyguard_hun_animator_start_tag);
-    private static final AnimationProperties KEYGUARD_HUN_PROPERTIES =
-            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-    @VisibleForTesting
-    final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
-            new KeyguardUpdateMonitorCallback() {
-
-                @Override
-                public void onBiometricAuthenticated(int userId,
-                        BiometricSourceType biometricSourceType) {
-                    if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
-                        mDelayShowingKeyguardStatusBar = true;
-                    }
-                }
-
-                @Override
-                public void onBiometricRunningStateChanged(boolean running,
-                        BiometricSourceType biometricSourceType) {
-                    boolean keyguardOrShadeLocked = mBarState == StatusBarState.KEYGUARD
-                            || mBarState == StatusBarState.SHADE_LOCKED;
-                    if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
-                            && !mDelayShowingKeyguardStatusBar) {
-                        mFirstBypassAttempt = false;
-                        animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-                    }
-                }
-
-                @Override
-                public void onFinishedGoingToSleep(int why) {
-                    mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
-                    mDelayShowingKeyguardStatusBar = false;
-                }
-            };
-    private final KeyguardStateController.Callback mKeyguardMonitorCallback =
-            new KeyguardStateController.Callback() {
-                @Override
-                public void onKeyguardFadingAwayChanged() {
-                    if (!mKeyguardStateController.isKeyguardFadingAway()) {
-                        mFirstBypassAttempt = false;
-                        mDelayShowingKeyguardStatusBar = false;
-                    }
-                }
-            };
-
-    private final InjectionInflationController mInjectionInflationController;
-    private final PowerManager mPowerManager;
-    private final AccessibilityManager mAccessibilityManager;
-    private final NotificationWakeUpCoordinator mWakeUpCoordinator;
-    private final PulseExpansionHandler mPulseExpansionHandler;
-    private final KeyguardBypassController mKeyguardBypassController;
-    private final KeyguardUpdateMonitor mUpdateMonitor;
 
     @VisibleForTesting
     protected KeyguardAffordanceHelper mAffordanceHelper;
-    private KeyguardUserSwitcher mKeyguardUserSwitcher;
-    @VisibleForTesting
-    protected KeyguardStatusBarView mKeyguardStatusBar;
-    @VisibleForTesting
-    protected ViewGroup mBigClockContainer;
-    private QS mQs;
-    @VisibleForTesting
-    protected FrameLayout mQsFrame;
-    @VisibleForTesting
-    protected KeyguardStatusView mKeyguardStatusView;
-    private View mQsNavbarScrim;
-    protected NotificationsQuickSettingsContainer mNotificationContainerParent;
-    protected NotificationStackScrollLayout mNotificationStackScroller;
-    protected FrameLayout mHomeControlsLayout;
-    private boolean mAnimateNextPositionUpdate;
 
-    private int mTrackingPointer;
-    private VelocityTracker mQsVelocityTracker;
-    private boolean mQsTracking;
-
-    /**
-     * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
-     * the expansion for quick settings.
-     */
-    private boolean mConflictingQsExpansionGesture;
-
-    /**
-     * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
-     * intercepted yet.
-     */
-    private boolean mIntercepting;
-    private boolean mPanelExpanded;
-    private boolean mQsExpanded;
-    private boolean mQsExpandedWhenExpandingStarted;
-    private boolean mQsFullyExpanded;
-    private boolean mKeyguardShowing;
-    private boolean mDozing;
-    private boolean mDozingOnDown;
-    protected int mBarState;
-    private float mInitialHeightOnTouch;
-    private float mInitialTouchX;
-    private float mInitialTouchY;
-    private float mLastTouchX;
-    private float mLastTouchY;
-    protected float mQsExpansionHeight;
-    protected int mQsMinExpansionHeight;
-    protected int mQsMaxExpansionHeight;
-    private int mQsPeekHeight;
-    private boolean mStackScrollerOverscrolling;
-    private boolean mQsExpansionFromOverscroll;
-    private float mLastOverscroll;
-    protected boolean mQsExpansionEnabled = true;
-    private ValueAnimator mQsExpansionAnimator;
-    private FlingAnimationUtils mFlingAnimationUtils;
-    private int mStatusBarMinHeight;
-    private int mNotificationsHeaderCollideDistance;
-    private int mUnlockMoveDistance;
-    private float mEmptyDragAmount;
-    private float mDownX;
-    private float mDownY;
-
-    private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm =
-            new KeyguardClockPositionAlgorithm();
-    private final KeyguardClockPositionAlgorithm.Result mClockPositionResult =
-            new KeyguardClockPositionAlgorithm.Result();
-    private boolean mIsExpanding;
-
-    private boolean mBlockTouches;
-    // Used for two finger gesture as well as accessibility shortcut to QS.
-    private boolean mQsExpandImmediate;
-    private boolean mTwoFingerQsExpandPossible;
-
-    /**
-     * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
-     * need to take this into account in our panel height calculation.
-     */
-    private boolean mQsAnimatorExpand;
-    private boolean mIsLaunchTransitionFinished;
-    private boolean mIsLaunchTransitionRunning;
-    private Runnable mLaunchAnimationEndRunnable;
-    private boolean mOnlyAffordanceInThisMotion;
-    private boolean mKeyguardStatusViewAnimating;
-    private ValueAnimator mQsSizeChangeAnimator;
-
-    private boolean mShowEmptyShadeView;
-
-    private boolean mQsScrimEnabled = true;
-    private boolean mLastAnnouncementWasQuickSettings;
-    private boolean mQsTouchAboveFalsingThreshold;
-    private int mQsFalsingThreshold;
-
-    private float mKeyguardStatusBarAnimateAlpha = 1f;
     private int mOldLayoutDirection;
-    private HeadsUpTouchHelper mHeadsUpTouchHelper;
-    private boolean mIsExpansionFromHeadsUp;
-    private boolean mListenForHeadsUp;
-    private int mNavigationBarBottomHeight;
-    private boolean mExpandingFromHeadsUp;
-    private boolean mCollapsedOnDown;
-    private int mPositionMinSideMargin;
-    private int mMaxFadeoutHeight;
-    private int mLastOrientation = -1;
-    private boolean mClosingWithAlphaFadeOut;
-    private boolean mHeadsUpAnimatingAway;
-    private boolean mLaunchingAffordance;
-    private boolean mAffordanceHasPreview;
-    private FalsingManager mFalsingManager;
-    private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
 
-    private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
-        @Override
-        public void run() {
-            setHeadsUpAnimatingAway(false);
-            notifyBarPanelExpansionChanged();
-        }
-    };
-    private NotificationGroupManager mGroupManager;
-    private boolean mShowIconsWhenExpanded;
-    private int mIndicationBottomPadding;
-    private int mAmbientIndicationBottomPadding;
-    private boolean mIsFullWidth;
-    private boolean mBlockingExpansionForCurrentTouch;
-
-    /**
-     * Following variables maintain state of events when input focus transfer may occur.
-     */
-    private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
-    private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
-
-    /**
-     * Current dark amount that follows regular interpolation curve of animation.
-     */
-    private float mInterpolatedDarkAmount;
-
-    /**
-     * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
-     * interpolation curve is different.
-     */
-    private float mLinearDarkAmount;
-
-    private boolean mPulsing;
-    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
-    private boolean mNoVisibleNotifications = true;
-    private boolean mUserSetupComplete;
-    private int mQsNotificationTopPadding;
-    private float mExpandOffset;
-    private boolean mHideIconsDuringNotificationLaunch = true;
-    private int mStackScrollerMeasuringPass;
-    private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
-            = new ArrayList<>();
-    private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
-    private HeadsUpAppearanceController mHeadsUpAppearanceController;
-
-    private int mPanelAlpha;
     private int mCurrentPanelAlpha;
     private final Paint mAlphaPaint = new Paint();
-    private Runnable mPanelAlphaEndAction;
-    private float mBottomAreaShadeAlpha;
-    private final ValueAnimator mBottomAreaShadeAlphaAnimator;
-    private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (mPanelAlphaEndAction != null) {
-                mPanelAlphaEndAction.run();
-            }
-        }
-    };
-    private final AnimatableProperty PANEL_ALPHA = AnimatableProperty.from(
-            "panelAlpha",
-            NotificationPanelView::setPanelAlphaInternal,
-            NotificationPanelView::getCurrentPanelAlpha,
-            R.id.panel_alpha_animator_tag,
-            R.id.panel_alpha_animator_start_tag,
-            R.id.panel_alpha_animator_end_tag);
-    private final AnimationProperties PANEL_ALPHA_OUT_PROPERTIES = new AnimationProperties()
-            .setDuration(150)
-            .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_OUT);
-    private final AnimationProperties PANEL_ALPHA_IN_PROPERTIES = new AnimationProperties()
-            .setDuration(200)
-            .setAnimationFinishListener(mAnimatorListenerAdapter)
-            .setCustomInterpolator(PANEL_ALPHA.getProperty(), Interpolators.ALPHA_IN);
-    private final NotificationEntryManager mEntryManager;
+    private boolean mDozing;
 
-    private final CommandQueue mCommandQueue;
-    private final NotificationLockscreenUserManager mLockscreenUserManager;
-    private final ShadeController mShadeController;
-    private int mDisplayId;
-
-    /**
-     * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
-     *
-     * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
-     * work, check the current id with the cached id.
-     */
-    private int mThemeResId;
-    private KeyguardIndicationController mKeyguardIndicationController;
-    private Consumer<Boolean> mAffordanceLaunchListener;
-    private int mShelfHeight;
-    private Runnable mOnReinflationListener;
-    private int mDarkIconSize;
-    private int mHeadsUpInset;
-    private boolean mHeadsUpPinnedMode;
-    private float mKeyguardHeadsUpShowingAmount = 0.0f;
-    private boolean mShowingKeyguardHeadsUp;
-    private boolean mAllowExpandForSmallExpansion;
-    private Runnable mExpandAfterLayoutRunnable;
-
-    /**
-     * If face auth with bypass is running for the first time after you turn on the screen.
-     * (From aod or screen off)
-     */
-    private boolean mFirstBypassAttempt;
-    /**
-     * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
-     * the keyguard is dismissed to show the status bar.
-     */
-    private boolean mDelayShowingKeyguardStatusBar;
-
-    private PluginManager mPluginManager;
-    private FrameLayout mPluginFrame;
-    private NPVPluginManager mNPVPluginManager;
-
-    @Inject
-    public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            InjectionInflationController injectionInflationController,
-            NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
-            DynamicPrivacyController dynamicPrivacyController,
-            KeyguardBypassController bypassController, FalsingManager falsingManager,
-            PluginManager pluginManager, ShadeController shadeController,
-            NotificationLockscreenUserManager notificationLockscreenUserManager,
-            NotificationEntryManager notificationEntryManager,
-            KeyguardStateController keyguardStateController,
-            StatusBarStateController statusBarStateController, DozeLog dozeLog,
-            DozeParameters dozeParameters, CommandQueue commandQueue) {
-        super(context, attrs, falsingManager, dozeLog, keyguardStateController,
-                (SysuiStatusBarStateController) statusBarStateController);
+    public NotificationPanelView(Context context, AttributeSet attrs) {
+        super(context, attrs);
         setWillNotDraw(!DEBUG);
-        mInjectionInflationController = injectionInflationController;
-        mFalsingManager = falsingManager;
-        mPowerManager = context.getSystemService(PowerManager.class);
-        mWakeUpCoordinator = coordinator;
-        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
-        setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
         mAlphaPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
-        setPanelAlpha(255, false /* animate */);
-        mCommandQueue = commandQueue;
-        mDisplayId = context.getDisplayId();
-        mPulseExpansionHandler = pulseExpansionHandler;
-        mDozeParameters = dozeParameters;
-        pulseExpansionHandler.setPulseExpandAbortListener(() -> {
-            if (mQs != null) {
-                mQs.animateHeaderSlidingOut();
-            }
-        });
-        mThemeResId = context.getThemeResId();
-        mKeyguardBypassController = bypassController;
-        mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
-        mKeyguardStateController.addCallback(mKeyguardMonitorCallback);
-        dynamicPrivacyController.addListener(this);
-
-        mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
-        mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
-            mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
-            updateKeyguardBottomAreaAlpha();
-        });
-        mBottomAreaShadeAlphaAnimator.setDuration(160);
-        mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
-        mPluginManager = pluginManager;
-        mShadeController = shadeController;
-        mLockscreenUserManager = notificationLockscreenUserManager;
-        mEntryManager = notificationEntryManager;
 
         setBackgroundColor(Color.TRANSPARENT);
     }
 
-    /**
-     * Returns if there's a custom clock being presented.
-     */
-    public boolean hasCustomClock() {
-        return mKeyguardStatusView.hasCustomClock();
-    }
-
-    private void setStatusBar(StatusBar bar) {
-        mStatusBar = bar;
-        mKeyguardBottomArea.setStatusBar(mStatusBar);
-    }
-
-    /**
-     * Call after this view has been fully inflated and had its children attached.
-     */
-    public void onChildrenAttached() {
-        loadDimens();
-        mKeyguardStatusBar = findViewById(R.id.keyguard_header);
-        mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
-
-        KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
-        mBigClockContainer = findViewById(R.id.big_clock_container);
-        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
-
-        mHomeControlsLayout = findViewById(R.id.home_controls_layout);
-        mNotificationContainerParent = findViewById(R.id.notification_container_parent);
-        mNotificationStackScroller = findViewById(R.id.notification_stack_scroller);
-        mNotificationStackScroller.setOnHeightChangedListener(this);
-        mNotificationStackScroller.setOverscrollTopChangedListener(this);
-        mNotificationStackScroller.setOnEmptySpaceClickListener(this);
-        addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
-        mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area);
-        mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
-        mLastOrientation = getResources().getConfiguration().orientation;
-        mPluginFrame = findViewById(R.id.plugin_frame);
-        if (Settings.System.getInt(
-                mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) {
-            mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
-        }
-
-
-        initBottomArea();
-
-        mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
-        mQsFrame = findViewById(R.id.qs_frame);
-        mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController);
-        mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
-            @Override
-            public void onFullyHiddenChanged(boolean isFullyHidden) {
-                updateKeyguardStatusBarForHeadsUp();
-            }
-
-            @Override
-            public void onPulseExpansionChanged(boolean expandingChanged) {
-                if (mKeyguardBypassController.getBypassEnabled()) {
-                    // Position the notifications while dragging down while pulsing
-                    requestScrollerTopPaddingUpdate(false /* animate */);
-                    updateQSPulseExpansion();
-                }
-            }
-        });
-
-        mPluginManager.addPluginListener(
-                new PluginListener<HomeControlsPlugin>() {
-
-                    @Override
-                    public void onPluginConnected(HomeControlsPlugin plugin,
-                                                  Context pluginContext) {
-                        plugin.sendParentGroup(mHomeControlsLayout);
-                    }
-
-                    @Override
-                    public void onPluginDisconnected(HomeControlsPlugin plugin) {
-
-                    }
-                }, HomeControlsPlugin.class, false);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        FragmentHostManager.get(this).addTagListener(QS.TAG, mFragmentListener);
-        Dependency.get(StatusBarStateController.class).addCallback(this);
-        Dependency.get(ZenModeController.class).addCallback(this);
-        Dependency.get(ConfigurationController.class).addCallback(this);
-        mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
-        // Theme might have changed between inflating this view and attaching it to the window, so
-        // force a call to onThemeChanged
-        onThemeChanged();
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        FragmentHostManager.get(this).removeTagListener(QS.TAG, mFragmentListener);
-        Dependency.get(StatusBarStateController.class).removeCallback(this);
-        Dependency.get(ZenModeController.class).removeCallback(this);
-        Dependency.get(ConfigurationController.class).removeCallback(this);
-        mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
-    }
-
-    @Override
-    protected void loadDimens() {
-        super.loadDimens();
-        mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.4f);
-        mStatusBarMinHeight = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
-        mQsPeekHeight = getResources().getDimensionPixelSize(R.dimen.qs_peek_height);
-        mNotificationsHeaderCollideDistance =
-                getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
-        mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
-        mClockPositionAlgorithm.loadDimens(getResources());
-        mQsFalsingThreshold = getResources().getDimensionPixelSize(
-                R.dimen.qs_falsing_threshold);
-        mPositionMinSideMargin = getResources().getDimensionPixelSize(
-                R.dimen.notification_panel_min_side_margin);
-        mMaxFadeoutHeight = getResources().getDimensionPixelSize(
-                R.dimen.max_notification_fadeout_height);
-        mIndicationBottomPadding = getResources().getDimensionPixelSize(
-                R.dimen.keyguard_indication_bottom_padding);
-        mQsNotificationTopPadding = getResources().getDimensionPixelSize(
-                R.dimen.qs_notification_padding);
-        mShelfHeight = getResources().getDimensionPixelSize(R.dimen.notification_shelf_height);
-        mDarkIconSize = getResources().getDimensionPixelSize(
-                R.dimen.status_bar_icon_drawing_size_dark);
-        int statusbarHeight = getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
-        mHeadsUpInset = statusbarHeight + getResources().getDimensionPixelSize(
-                R.dimen.heads_up_status_bar_padding);
-        mQqsSplitFraction = ((float) getResources().getInteger(R.integer.qqs_split_fraction)) / (
-                getResources().getInteger(R.integer.qqs_split_fraction)
-                        + getResources().getInteger(R.integer.qs_split_fraction));
-    }
-
-    /**
-     * @see #launchCamera(boolean, int)
-     * @see #setLaunchingAffordance(boolean)
-     */
-    public void setLaunchAffordanceListener(Consumer<Boolean> listener) {
-        mAffordanceLaunchListener = listener;
-    }
-
-    public void updateResources() {
-        Resources res = getResources();
-        int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
-        int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
-        FrameLayout.LayoutParams lp =
-                (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
-        if (lp.width != qsWidth || lp.gravity != panelGravity) {
-            lp.width = qsWidth;
-            lp.gravity = panelGravity;
-            mQsFrame.setLayoutParams(lp);
-        }
-
-        int panelWidth = res.getDimensionPixelSize(R.dimen.notification_panel_width);
-        lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
-        if (lp.width != panelWidth || lp.gravity != panelGravity) {
-            lp.width = panelWidth;
-            lp.gravity = panelGravity;
-            mNotificationStackScroller.setLayoutParams(lp);
-        }
-        int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
-        int topMargin = sideMargin;
-        lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
-        if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
-                || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
-            lp.width = qsWidth;
-            lp.gravity = panelGravity;
-            lp.leftMargin = sideMargin;
-            lp.rightMargin = sideMargin;
-            lp.topMargin = topMargin;
-            mPluginFrame.setLayoutParams(lp);
-        }
-    }
-
-    @Override
-    public void onDensityOrFontScaleChanged() {
-        updateShowEmptyShadeView();
-    }
-
-    @Override
-    public void onThemeChanged() {
-        final int themeResId = getContext().getThemeResId();
-        if (mThemeResId == themeResId) {
-            return;
-        }
-        mThemeResId = themeResId;
-
-        reInflateViews();
-    }
-
-    @Override
-    public void onOverlayChanged() {
-        reInflateViews();
-    }
-
-    private void reInflateViews() {
-        updateShowEmptyShadeView();
-
-        // Re-inflate the status view group.
-        int index = indexOfChild(mKeyguardStatusView);
-        removeView(mKeyguardStatusView);
-        mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController
-                .injectable(LayoutInflater.from(mContext)).inflate(
-                        R.layout.keyguard_status_view,
-                        this,
-                        false);
-        addView(mKeyguardStatusView, index);
-
-        // Re-associate the clock container with the keyguard clock switch.
-        mBigClockContainer.removeAllViews();
-        KeyguardClockSwitch keyguardClockSwitch = findViewById(R.id.keyguard_clock_container);
-        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
-
-        // Update keyguard bottom area
-        index = indexOfChild(mKeyguardBottomArea);
-        removeView(mKeyguardBottomArea);
-        KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
-        mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController
-                .injectable(LayoutInflater.from(mContext)).inflate(
-                        R.layout.keyguard_bottom_area,
-                        this,
-                        false);
-        mKeyguardBottomArea.initFrom(oldBottomArea);
-        addView(mKeyguardBottomArea, index);
-        initBottomArea();
-        mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
-        onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
-                mStatusBarStateController.getInterpolatedDozeAmount());
-
-        if (mKeyguardStatusBar != null) {
-            mKeyguardStatusBar.onThemeChanged();
-        }
-
-        setKeyguardStatusViewVisibility(mBarState, false, false);
-        setKeyguardBottomAreaVisibility(mBarState, false);
-        if (mOnReinflationListener != null) {
-            mOnReinflationListener.run();
-        }
-        reinflatePluginContainer();
-    }
-
-    @Override
-    public void onUiModeChanged() {
-        reinflatePluginContainer();
-    }
-
-    private void reinflatePluginContainer() {
-        int index = indexOfChild(mPluginFrame);
-        removeView(mPluginFrame);
-        mPluginFrame = (FrameLayout) mInjectionInflationController
-                .injectable(LayoutInflater.from(mContext)).inflate(
-                        R.layout.status_bar_expanded_plugin_frame,
-                        this,
-                        false);
-        addView(mPluginFrame, index);
-
-        Resources res = getResources();
-        int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
-        int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
-        FrameLayout.LayoutParams lp;
-        int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
-        int topMargin =
-                res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
-        if (Utils.useQsMediaPlayer(mContext)) {
-            topMargin = res.getDimensionPixelOffset(
-                    com.android.internal.R.dimen.quick_qs_total_height_with_media);
-        }
-        lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
-        if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
-                || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
-            lp.width = qsWidth;
-            lp.gravity = panelGravity;
-            lp.leftMargin = sideMargin;
-            lp.rightMargin = sideMargin;
-            lp.topMargin = topMargin;
-            mPluginFrame.setLayoutParams(lp);
-        }
-
-        if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
-    }
-
-    private void initBottomArea() {
-        mAffordanceHelper = new KeyguardAffordanceHelper(this, getContext(), mFalsingManager);
-        mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
-        mKeyguardBottomArea.setStatusBar(mStatusBar);
-        mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
-    }
-
-    public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
-        mKeyguardIndicationController = indicationController;
-        mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
-        super.onLayout(changed, left, top, right, bottom);
-        setIsFullWidth(mNotificationStackScroller.getWidth() == getWidth());
-
-        // Update Clock Pivot
-        mKeyguardStatusView.setPivotX(getWidth() / 2);
-        mKeyguardStatusView.setPivotY((FONT_HEIGHT - CAP_HEIGHT) / 2048f *
-                mKeyguardStatusView.getClockTextSize());
-
-        // Calculate quick setting heights.
-        int oldMaxHeight = mQsMaxExpansionHeight;
-        if (mQs != null) {
-            mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
-            if (mNPVPluginManager != null) {
-                mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
-                mQsMinExpansionHeight += mNPVPluginManager.getHeight();
-            }
-            mQsMaxExpansionHeight = mQs.getDesiredHeight();
-            mNotificationStackScroller.setMaxTopPadding(
-                    mQsMaxExpansionHeight + mQsNotificationTopPadding);
-        }
-        positionClockAndNotifications();
-        if (mQsExpanded && mQsFullyExpanded) {
-            mQsExpansionHeight = mQsMaxExpansionHeight;
-            requestScrollerTopPaddingUpdate(false /* animate */);
-            requestPanelHeightUpdate();
-
-            // Size has changed, start an animation.
-            if (mQsMaxExpansionHeight != oldMaxHeight) {
-                startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
-            }
-        } else if (!mQsExpanded) {
-            setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
-        }
-        updateExpandedHeight(getExpandedHeight());
-        updateHeader();
-
-        // If we are running a size change animation, the animation takes care of the height of
-        // the container. However, if we are not animating, we always need to make the QS container
-        // the desired height so when closing the QS detail, it stays smaller after the size change
-        // animation is finished but the detail view is still being animated away (this animation
-        // takes longer than the size change animation).
-        if (mQsSizeChangeAnimator == null && mQs != null) {
-            mQs.setHeightOverride(mQs.getDesiredHeight());
-        }
-        updateMaxHeadsUpTranslation();
-        updateGestureExclusionRect();
-        if (mExpandAfterLayoutRunnable != null) {
-            mExpandAfterLayoutRunnable.run();
-            mExpandAfterLayoutRunnable = null;
-        }
-        DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
-    }
-
-    private void updateGestureExclusionRect() {
-        Rect exclusionRect = calculateGestureExclusionRect();
-        setSystemGestureExclusionRects(exclusionRect.isEmpty()
-                ? Collections.EMPTY_LIST
-                : Collections.singletonList(exclusionRect));
-    }
-
-    private Rect calculateGestureExclusionRect() {
-        Rect exclusionRect = null;
-        Region touchableRegion = mHeadsUpManager.calculateTouchableRegion();
-        if (isFullyCollapsed() && touchableRegion != null) {
-            // Note: The heads up manager also calculates the non-pinned touchable region
-            exclusionRect = touchableRegion.getBounds();
-        }
-        return exclusionRect != null
-                ? exclusionRect
-                : mEmptyRect;
-    }
-
-    private void setIsFullWidth(boolean isFullWidth) {
-        mIsFullWidth = isFullWidth;
-        mNotificationStackScroller.setIsFullWidth(isFullWidth);
-    }
-
-    private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
-        if (mQsSizeChangeAnimator != null) {
-            oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
-            mQsSizeChangeAnimator.cancel();
-        }
-        mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
-        mQsSizeChangeAnimator.setDuration(300);
-        mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                requestScrollerTopPaddingUpdate(false /* animate */);
-                requestPanelHeightUpdate();
-                int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
-                mQs.setHeightOverride(height);
-            }
-        });
-        mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mQsSizeChangeAnimator = null;
-            }
-        });
-        mQsSizeChangeAnimator.start();
-    }
-
-    /**
-     * Positions the clock and notifications dynamically depending on how many notifications are
-     * showing.
-     */
-    private void positionClockAndNotifications() {
-        boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
-        boolean animateClock = animate || mAnimateNextPositionUpdate;
-        int stackScrollerPadding;
-        if (mBarState != StatusBarState.KEYGUARD) {
-            stackScrollerPadding = getUnlockedStackScrollerPadding();
-        } else {
-            int totalHeight = getHeight();
-            int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
-            int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
-            boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
-            final boolean hasVisibleNotifications =
-                    !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
-            mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
-            mClockPositionAlgorithm.setup(
-                    mStatusBarMinHeight,
-                    totalHeight - bottomPadding,
-                    mNotificationStackScroller.getIntrinsicContentHeight(),
-                    getExpandedFraction(),
-                    totalHeight,
-                    (int) (mKeyguardStatusView.getHeight()
-                            - mShelfHeight / 2.0f - mDarkIconSize / 2.0f),
-                    clockPreferredY,
-                    hasCustomClock(),
-                    hasVisibleNotifications,
-                    mInterpolatedDarkAmount,
-                    mEmptyDragAmount,
-                    bypassEnabled,
-                    getUnlockedStackScrollerPadding());
-            mClockPositionAlgorithm.run(mClockPositionResult);
-            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
-                    mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
-            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
-                    mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
-            updateNotificationTranslucency();
-            updateClock();
-            stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
-        }
-        mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
-
-        mStackScrollerMeasuringPass++;
-        requestScrollerTopPaddingUpdate(animate);
-        mStackScrollerMeasuringPass = 0;
-        mAnimateNextPositionUpdate = false;
-    }
-
-    /**
-     * @return the padding of the stackscroller when unlocked
-     */
-    private int getUnlockedStackScrollerPadding() {
-        return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
-                + mQsNotificationTopPadding;
-    }
-
-    /**
-     * @param maximum the maximum to return at most
-     * @return the maximum keyguard notifications that can fit on the screen
-     */
-    public int computeMaxKeyguardNotifications(int maximum) {
-        float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
-        int notificationPadding = Math.max(1, getResources().getDimensionPixelSize(
-                R.dimen.notification_divider_height));
-        NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
-        float shelfSize = shelf.getVisibility() == GONE ? 0
-                : shelf.getIntrinsicHeight() + notificationPadding;
-        float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize
-                - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
-                - mKeyguardStatusView.getLogoutButtonHeight();
-        int count = 0;
-        for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
-            ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
-            if (!(child instanceof ExpandableNotificationRow)) {
-                continue;
-            }
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            boolean suppressedSummary = mGroupManager != null
-                    && mGroupManager.isSummaryOfSuppressedGroup(row.getEntry().getSbn());
-            if (suppressedSummary) {
-                continue;
-            }
-            if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
-                continue;
-            }
-            if (row.isRemoved()) {
-                continue;
-            }
-            availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
-                    + notificationPadding;
-            if (availableSpace >= 0 && count < maximum) {
-                count++;
-            } else if (availableSpace > -shelfSize) {
-                // if we are exactly the last view, then we can show us still!
-                for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
-                    if (mNotificationStackScroller.getChildAt(j)
-                            instanceof ExpandableNotificationRow) {
-                        return count;
-                    }
-                }
-                count++;
-                return count;
-            } else {
-                return count;
-            }
-        }
-        return count;
-    }
-
-    private void updateClock() {
-        if (!mKeyguardStatusViewAnimating) {
-            mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
-        }
-    }
-
-    public void animateToFullShade(long delay) {
-        mNotificationStackScroller.goToFullShade(delay);
-        requestLayout();
-        mAnimateNextPositionUpdate = true;
-    }
-
-    public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
-        mQsExpansionEnabled = qsExpansionEnabled;
-        if (mQs == null) return;
-        mQs.setHeaderClickable(qsExpansionEnabled);
-    }
-
-    @Override
-    public void resetViews(boolean animate) {
-        mIsLaunchTransitionFinished = false;
-        mBlockTouches = false;
-        if (!mLaunchingAffordance) {
-            mAffordanceHelper.reset(false);
-            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
-        }
-        mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
-                true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
-        if (animate) {
-            animateCloseQs(true /* animateAway */);
-        } else {
-            closeQs();
-        }
-        mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
-                !animate /* cancelAnimators */);
-        mNotificationStackScroller.resetScrollPosition();
-    }
-
-    @Override
-    public void collapse(boolean delayed, float speedUpFactor) {
-        if (!canPanelBeCollapsed()) {
-            return;
-        }
-
-        if (mQsExpanded) {
-            mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
-        }
-        super.collapse(delayed, speedUpFactor);
-    }
-
-    public void closeQs() {
-        cancelQsAnimation();
-        setQsExpansion(mQsMinExpansionHeight);
-    }
-
-    /**
-     * Animate QS closing by flinging it.
-     * If QS is expanded, it will collapse into QQS and stop.
-     *
-     * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
-     */
-    public void animateCloseQs(boolean animateAway) {
-        if (mQsExpansionAnimator != null) {
-            if (!mQsAnimatorExpand) {
-                return;
-            }
-            float height = mQsExpansionHeight;
-            mQsExpansionAnimator.cancel();
-            setQsExpansion(height);
-        }
-        flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
-    }
-
-    public void expandWithQs() {
-        if (mQsExpansionEnabled) {
-            mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
-        }
-        if (isFullyCollapsed()) {
-            expand(true /* animate */);
-        } else {
-            flingSettings(0 /* velocity */, FLING_EXPAND);
-        }
-    }
-
-    public void expandWithoutQs() {
-        if (isQsExpanded()) {
-            flingSettings(0 /* velocity */, FLING_COLLAPSE);
-        } else {
-            expand(true /* animate */);
-        }
-    }
-
-    @Override
-    public void fling(float vel, boolean expand) {
-        GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
-        if (gr != null) {
-            gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
-        }
-        super.fling(vel, expand);
-    }
-
-    @Override
-    protected void flingToHeight(float vel, boolean expand, float target,
-            float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
-        mHeadsUpTouchHelper.notifyFling(!expand);
-        setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
-        super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
-            return false;
-        }
-        initDownStates(event);
-        // Do not let touches go to shade or QS if the bouncer is visible,
-        // but still let user swipe down to expand the panel, dismissing the bouncer.
-        if (mStatusBar.isBouncerShowing()) {
-            return true;
-        }
-        if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
-            mIsExpansionFromHeadsUp = true;
-            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
-            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
-            return true;
-        }
-        if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
-                && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
-            return true;
-        }
-
-        if (!isFullyCollapsed() && onQsIntercept(event)) {
-            return true;
-        }
-        return super.onInterceptTouchEvent(event);
-    }
-
-    private boolean onQsIntercept(MotionEvent event) {
-        int pointerIndex = event.findPointerIndex(mTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mTrackingPointer = event.getPointerId(pointerIndex);
-        }
-        final float x = event.getX(pointerIndex);
-        final float y = event.getY(pointerIndex);
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mIntercepting = true;
-                mInitialTouchY = y;
-                mInitialTouchX = x;
-                initVelocityTracker();
-                trackMovement(event);
-                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
-                    getParent().requestDisallowInterceptTouchEvent(true);
-                }
-                if (mQsExpansionAnimator != null) {
-                    onQsExpansionStarted();
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    mQsTracking = true;
-                    mIntercepting = false;
-                    mNotificationStackScroller.cancelLongPress();
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    mTrackingPointer = event.getPointerId(newIndex);
-                    mInitialTouchX = event.getX(newIndex);
-                    mInitialTouchY = event.getY(newIndex);
-                }
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                final float h = y - mInitialTouchY;
-                trackMovement(event);
-                if (mQsTracking) {
-
-                    // Already tracking because onOverscrolled was called. We need to update here
-                    // so we don't stop for a frame until the next touch event gets handled in
-                    // onTouchEvent.
-                    setQsExpansion(h + mInitialHeightOnTouch);
-                    trackMovement(event);
-                    mIntercepting = false;
-                    return true;
-                }
-                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
-                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
-                    mQsTracking = true;
-                    onQsExpansionStarted();
-                    notifyExpandingFinished();
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    mInitialTouchY = y;
-                    mInitialTouchX = x;
-                    mIntercepting = false;
-                    mNotificationStackScroller.cancelLongPress();
-                    return true;
-                }
-                break;
-
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                trackMovement(event);
-                if (mQsTracking) {
-                    flingQsWithCurrentVelocity(y,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                    mQsTracking = false;
-                }
-                mIntercepting = false;
-                break;
-        }
-        return false;
-    }
-
-    @Override
-    protected boolean isInContentBounds(float x, float y) {
-        float stackScrollerX = mNotificationStackScroller.getX();
-        return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
-                && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
-    }
-
-    private void initDownStates(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mOnlyAffordanceInThisMotion = false;
-            mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
-            mDozingOnDown = isDozing();
-            mDownX = event.getX();
-            mDownY = event.getY();
-            mCollapsedOnDown = isFullyCollapsed();
-            mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
-            mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
-            mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
-            if (mExpectingSynthesizedDown) {
-                mLastEventSynthesizedDown = true;
-            } else {
-                // down but not synthesized motion event.
-                mLastEventSynthesizedDown = false;
-            }
-        } else {
-            // not down event at all.
-            mLastEventSynthesizedDown = false;
-        }
-    }
-
-    private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
-        float vel = getCurrentQSVelocity();
-        final boolean expandsQs = flingExpandsQs(vel);
-        if (expandsQs) {
-            logQsSwipeDown(y);
-        }
-        flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
-    }
-
-    private void logQsSwipeDown(float y) {
-        float vel = getCurrentQSVelocity();
-        final int gesture = mBarState == StatusBarState.KEYGUARD
-                ? MetricsEvent.ACTION_LS_QS
-                : MetricsEvent.ACTION_SHADE_QS_PULL;
-        mLockscreenGestureLogger.write(gesture,
-                (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
-                (int) (vel / mStatusBar.getDisplayDensity()));
-    }
-
-    private boolean flingExpandsQs(float vel) {
-        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
-            return false;
-        }
-        if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            return getQsExpansionFraction() > 0.5f;
-        } else {
-            return vel > 0;
-        }
-    }
-
-    private boolean isFalseTouch() {
-        if (!needsAntiFalsing()) {
-            return false;
-        }
-        if (mFalsingManager.isClassiferEnabled()) {
-            return mFalsingManager.isFalseTouch();
-        }
-        return !mQsTouchAboveFalsingThreshold;
-    }
-
-    private float getQsExpansionFraction() {
-        return Math.min(1f, (mQsExpansionHeight - mQsMinExpansionHeight)
-                / (mQsMaxExpansionHeight - mQsMinExpansionHeight));
-    }
-
-    @Override
-    protected boolean shouldExpandWhenNotFlinging() {
-        if (super.shouldExpandWhenNotFlinging()) {
-            return true;
-        }
-        if (mAllowExpandForSmallExpansion) {
-            // When we get a touch that came over from launcher, the velocity isn't always correct
-            // Let's err on expanding if the gesture has been reasonably slow
-            long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
-            return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
-        }
-        return false;
-    }
-
-    @Override
-    protected float getOpeningHeight() {
-        return mNotificationStackScroller.getOpeningHeight();
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
-            return false;
-        }
-
-        // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able to
-        // pull down QS or expand the shade.
-        if (mStatusBar.isBouncerShowingScrimmed()) {
-            return false;
-        }
-
-        // Make sure the next touch won't the blocked after the current ends.
-        if (event.getAction() == MotionEvent.ACTION_UP
-                || event.getAction() == MotionEvent.ACTION_CANCEL) {
-            mBlockingExpansionForCurrentTouch = false;
-        }
-        // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
-        // without any ACTION_MOVE event.
-        // In such case, simply expand the panel instead of being stuck at the bottom bar.
-        if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
-            expand(true /* animate */);
-        }
-        initDownStates(event);
-        if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
-                && mPulseExpansionHandler.onTouchEvent(event)) {
-            // We're expanding all the other ones shouldn't get this anymore
-            return true;
-        }
-        if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
-                && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
-            mIsExpansionFromHeadsUp = true;
-            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_PEEK, 1);
-        }
-        boolean handled = false;
-        if ((!mIsExpanding || mHintAnimationRunning)
-                && !mQsExpanded
-                && mBarState != StatusBarState.SHADE
-                && !mDozing) {
-            handled |= mAffordanceHelper.onTouchEvent(event);
-        }
-        if (mOnlyAffordanceInThisMotion) {
-            return true;
-        }
-        handled |= mHeadsUpTouchHelper.onTouchEvent(event);
-
-        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
-            return true;
-        }
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
-            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN, 1);
-            updateVerticalPanelPosition(event.getX());
-            handled = true;
-        }
-        handled |= super.onTouchEvent(event);
-        return !mDozing || mPulsing || handled;
-    }
-
-    private boolean handleQsTouch(MotionEvent event) {
-        final int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
-                && mBarState != StatusBarState.KEYGUARD && !mQsExpanded
-                && mQsExpansionEnabled) {
-
-            // Down in the empty area while fully expanded - go to QS.
-            mQsTracking = true;
-            mConflictingQsExpansionGesture = true;
-            onQsExpansionStarted();
-            mInitialHeightOnTouch = mQsExpansionHeight;
-            mInitialTouchY = event.getX();
-            mInitialTouchX = event.getY();
-        }
-        if (!isFullyCollapsed()) {
-            handleQsDown(event);
-        }
-        if (!mQsExpandImmediate && mQsTracking) {
-            onQsTouch(event);
-            if (!mConflictingQsExpansionGesture) {
-                return true;
-            }
-        }
-        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
-            mConflictingQsExpansionGesture = false;
-        }
-        if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed()
-                && mQsExpansionEnabled) {
-            mTwoFingerQsExpandPossible = true;
-        }
-        if (mTwoFingerQsExpandPossible && isOpenQsEvent(event)
-                && event.getY(event.getActionIndex()) < mStatusBarMinHeight) {
-            MetricsLogger.count(mContext, COUNTER_PANEL_OPEN_QS, 1);
-            mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
-            requestPanelHeightUpdate();
-
-            // Normally, we start listening when the panel is expanded, but here we need to start
-            // earlier so the state is already up to date when dragging down.
-            setListening(true);
-        }
-        if (isQsSplitEnabled() && !mKeyguardShowing) {
-            if (mQsExpandImmediate) {
-                mNotificationStackScroller.setVisibility(View.GONE);
-                mQsFrame.setVisibility(View.VISIBLE);
-                mHomeControlsLayout.setVisibility(View.VISIBLE);
-            } else {
-                mNotificationStackScroller.setVisibility(View.VISIBLE);
-                mQsFrame.setVisibility(View.GONE);
-                mHomeControlsLayout.setVisibility(View.GONE);
-            }
-        }
-        return false;
-    }
-
-    private boolean isInQsArea(float x, float y) {
-        return (x >= mQsFrame.getX()
-                && x <= mQsFrame.getX() + mQsFrame.getWidth())
-                && (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
-                || y <= mQs.getView().getY() + mQs.getView().getHeight());
-    }
-
-    private boolean isOnQsEndArea(float x) {
-        if (!isQsSplitEnabled()) return false;
-        if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
-            return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth()
-                    && x <= mQsFrame.getX() + mQsFrame.getWidth();
-        } else {
-            return x >= mQsFrame.getX()
-                    && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth();
-        }
-    }
-
-    private boolean isOpenQsEvent(MotionEvent event) {
-        final int pointerCount = event.getPointerCount();
-        final int action = event.getActionMasked();
-
-        final boolean twoFingerDrag = action == MotionEvent.ACTION_POINTER_DOWN
-                && pointerCount == 2;
-
-        final boolean stylusButtonClickDrag = action == MotionEvent.ACTION_DOWN
-                && (event.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY)
-                || event.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY));
-
-        final boolean mouseButtonClickDrag = action == MotionEvent.ACTION_DOWN
-                && (event.isButtonPressed(MotionEvent.BUTTON_SECONDARY)
-                || event.isButtonPressed(MotionEvent.BUTTON_TERTIARY));
-
-        final boolean onHeaderRight = isOnQsEndArea(event.getX());
-
-        return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight;
-    }
-
-    private void handleQsDown(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN
-                && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) {
-            mFalsingManager.onQsDown();
-            mQsTracking = true;
-            onQsExpansionStarted();
-            mInitialHeightOnTouch = mQsExpansionHeight;
-            mInitialTouchY = event.getX();
-            mInitialTouchX = event.getY();
-
-            // If we interrupt an expansion gesture here, make sure to update the state correctly.
-            notifyExpandingFinished();
-        }
-    }
-
-    /**
-     * Input focus transfer is about to happen.
-     */
-    public void startWaitingForOpenPanelGesture() {
-        if (!isFullyCollapsed()) {
-            return;
-        }
-        mExpectingSynthesizedDown = true;
-        onTrackingStarted();
-        updatePanelExpanded();
-    }
-
-    /**
-     * Called when this view is no longer waiting for input focus transfer.
-     *
-     * There are two scenarios behind this function call. First, input focus transfer
-     * has successfully happened and this view already received synthetic DOWN event.
-     * (mExpectingSynthesizedDown == false). Do nothing.
-     *
-     * Second, before input focus transfer finished, user may have lifted finger
-     * in previous window and this window never received synthetic DOWN event.
-     * (mExpectingSynthesizedDown == true).
-     * In this case, we use the velocity to trigger fling event.
-     *
-     * @param velocity unit is in px / millis
-     */
-    public void stopWaitingForOpenPanelGesture(final float velocity) {
-        if (mExpectingSynthesizedDown) {
-            mExpectingSynthesizedDown = false;
-            maybeVibrateOnOpening();
-            Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0,
-                    true /* expand */);
-            if (mStatusBar.getStatusBarWindow().getHeight()
-                    != mStatusBar.getStatusBarHeight()) {
-                // The panel is already expanded to its full size, let's expand directly
-                runnable.run();
-            } else {
-                mExpandAfterLayoutRunnable = runnable;
-            }
-            onTrackingStopped(false);
-        }
-    }
-
-    @Override
-    protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
-        boolean expands = super.flingExpands(vel, vectorVel, x, y);
-
-        // If we are already running a QS expansion, make sure that we keep the panel open.
-        if (mQsExpansionAnimator != null) {
-            expands = true;
-        }
-        return expands;
-    }
-
-    @Override
-    protected boolean shouldGestureWaitForTouchSlop() {
-        if (mExpectingSynthesizedDown) {
-            mExpectingSynthesizedDown = false;
-            return false;
-        }
-        return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
-    }
-
-    @Override
-    protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
-        return !mAffordanceHelper.isOnAffordanceIcon(x, y);
-    }
-
-    private void onQsTouch(MotionEvent event) {
-        int pointerIndex = event.findPointerIndex(mTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mTrackingPointer = event.getPointerId(pointerIndex);
-        }
-        final float y = event.getY(pointerIndex);
-        final float x = event.getX(pointerIndex);
-        final float h = y - mInitialTouchY;
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mQsTracking = true;
-                mInitialTouchY = y;
-                mInitialTouchX = x;
-                onQsExpansionStarted();
-                mInitialHeightOnTouch = mQsExpansionHeight;
-                initVelocityTracker();
-                trackMovement(event);
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    final float newY = event.getY(newIndex);
-                    final float newX = event.getX(newIndex);
-                    mTrackingPointer = event.getPointerId(newIndex);
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    mInitialTouchY = newY;
-                    mInitialTouchX = newX;
-                }
-                break;
-
-            case MotionEvent.ACTION_MOVE:
-                setQsExpansion(h + mInitialHeightOnTouch);
-                if (h >= getFalsingThreshold()) {
-                    mQsTouchAboveFalsingThreshold = true;
-                }
-                trackMovement(event);
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                mQsTracking = false;
-                mTrackingPointer = -1;
-                trackMovement(event);
-                float fraction = getQsExpansionFraction();
-                if (fraction != 0f || y >= mInitialTouchY) {
-                    flingQsWithCurrentVelocity(y,
-                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
-                }
-                if (mQsVelocityTracker != null) {
-                    mQsVelocityTracker.recycle();
-                    mQsVelocityTracker = null;
-                }
-                break;
-        }
-    }
-
-    private int getFalsingThreshold() {
-        float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
-        return (int) (mQsFalsingThreshold * factor);
-    }
-
-    @Override
-    public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
-        cancelQsAnimation();
-        if (!mQsExpansionEnabled) {
-            amount = 0f;
-        }
-        float rounded = amount >= 1f ? amount : 0f;
-        setOverScrolling(rounded != 0f && isRubberbanded);
-        mQsExpansionFromOverscroll = rounded != 0f;
-        mLastOverscroll = rounded;
-        updateQsState();
-        setQsExpansion(mQsMinExpansionHeight + rounded);
-    }
-
-    @Override
-    public void flingTopOverscroll(float velocity, boolean open) {
-        mLastOverscroll = 0f;
-        mQsExpansionFromOverscroll = false;
-        setQsExpansion(mQsExpansionHeight);
-        flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
-                open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE,
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        mStackScrollerOverscrolling = false;
-                        setOverScrolling(false);
-                        updateQsState();
-                    }
-                }, false /* isClick */);
-    }
-
-    private void setOverScrolling(boolean overscrolling) {
-        mStackScrollerOverscrolling = overscrolling;
-        if (mQs == null) return;
-        mQs.setOverscrolling(overscrolling);
-    }
-
-    private void onQsExpansionStarted() {
-        onQsExpansionStarted(0);
-    }
-
-    protected void onQsExpansionStarted(int overscrollAmount) {
-        cancelQsAnimation();
-        cancelHeightAnimator();
-
-        // Reset scroll position and apply that position to the expanded height.
-        float height = mQsExpansionHeight - overscrollAmount;
-        setQsExpansion(height);
-        requestPanelHeightUpdate();
-        mNotificationStackScroller.checkSnoozeLeavebehind();
-
-        // When expanding QS, let's authenticate the user if possible,
-        // this will speed up notification actions.
-        if (height == 0) {
-            mStatusBar.requestFaceAuth();
-        }
-    }
-
-    private void setQsExpanded(boolean expanded) {
-        boolean changed = mQsExpanded != expanded;
-        if (changed) {
-            mQsExpanded = expanded;
-            updateQsState();
-            requestPanelHeightUpdate();
-            mFalsingManager.setQsExpanded(expanded);
-            mStatusBar.setQsExpanded(expanded);
-            mNotificationContainerParent.setQsExpanded(expanded);
-            mPulseExpansionHandler.setQsExpanded(expanded);
-            mKeyguardBypassController.setQSExpanded(expanded);
-        }
-    }
-
-    @Override
-    public void onStateChanged(int statusBarState) {
-        boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
-        boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
-        int oldState = mBarState;
-        boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
-        setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
-        setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
-
-        mBarState = statusBarState;
-        mKeyguardShowing = keyguardShowing;
-        if (mKeyguardShowing && isQsSplitEnabled()) {
-            mNotificationStackScroller.setVisibility(View.VISIBLE);
-            mQsFrame.setVisibility(View.VISIBLE);
-            mHomeControlsLayout.setVisibility(View.GONE);
-        }
-
-        if (oldState == StatusBarState.KEYGUARD
-                && (goingToFullShade || statusBarState == StatusBarState.SHADE_LOCKED)) {
-            animateKeyguardStatusBarOut();
-            long delay = mBarState == StatusBarState.SHADE_LOCKED
-                    ? 0 : mKeyguardStateController.calculateGoingToFullShadeDelay();
-            mQs.animateHeaderSlidingIn(delay);
-        } else if (oldState == StatusBarState.SHADE_LOCKED
-                && statusBarState == StatusBarState.KEYGUARD) {
-            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            mNotificationStackScroller.resetScrollPosition();
-            // Only animate header if the header is visible. If not, it will partially animate out
-            // the top of QS
-            if (!mQsExpanded) {
-                mQs.animateHeaderSlidingOut();
-            }
-        } else {
-            mKeyguardStatusBar.setAlpha(1f);
-            mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
-            ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing);
-            if (keyguardShowing && oldState != mBarState) {
-                if (mQs != null) {
-                    mQs.hideImmediately();
-                }
-            }
-        }
-        updateKeyguardStatusBarForHeadsUp();
-        if (keyguardShowing) {
-            updateDozingVisibilities(false /* animate */);
-        }
-        // THe update needs to happen after the headerSlide in above, otherwise the translation
-        // would reset
-        updateQSPulseExpansion();
-        maybeAnimateBottomAreaAlpha();
-        resetHorizontalPanelPosition();
-        updateQsState();
-    }
-
-    private void maybeAnimateBottomAreaAlpha() {
-        mBottomAreaShadeAlphaAnimator.cancel();
-        if (mBarState == StatusBarState.SHADE_LOCKED) {
-            mBottomAreaShadeAlphaAnimator.start();
-        } else {
-            mBottomAreaShadeAlpha = 1f;
-        }
-    }
-
-    private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusViewAnimating = false;
-            mKeyguardStatusView.setVisibility(View.INVISIBLE);
-        }
-    };
-
-    private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusViewAnimating = false;
-            mKeyguardStatusView.setVisibility(View.GONE);
-        }
-    };
-
-    private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusViewAnimating = false;
-        }
-    };
-
-    private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardStatusBar.setVisibility(View.INVISIBLE);
-            mKeyguardStatusBar.setAlpha(1f);
-            mKeyguardStatusBarAnimateAlpha = 1f;
-        }
-    };
-
-    private void animateKeyguardStatusBarOut() {
-        ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
-        anim.addUpdateListener(mStatusBarAnimateAlphaListener);
-        anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
-                ? mKeyguardStateController.getKeyguardFadingAwayDelay()
-                : 0);
-
-        long duration;
-        if (mKeyguardStateController.isKeyguardFadingAway()) {
-            duration = mKeyguardStateController.getShortenedFadingAwayDuration();
-        } else {
-            duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
-        }
-        anim.setDuration(duration);
-
-        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
-            }
-        });
-        anim.start();
-    }
-
-    private final ValueAnimator.AnimatorUpdateListener mStatusBarAnimateAlphaListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
-                    updateHeaderKeyguardAlpha();
-                }
-            };
-
-    private void animateKeyguardStatusBarIn(long duration) {
-        mKeyguardStatusBar.setVisibility(View.VISIBLE);
-        mKeyguardStatusBar.setAlpha(0f);
-        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.addUpdateListener(mStatusBarAnimateAlphaListener);
-        anim.setDuration(duration);
-        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        anim.start();
-    }
-
-    private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
-        @Override
-        public void run() {
-            mKeyguardBottomArea.setVisibility(View.GONE);
-        }
-    };
-
-    private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
-        mKeyguardBottomArea.animate().cancel();
-        if (goingToFullShade) {
-            mKeyguardBottomArea.animate()
-                    .alpha(0f)
-                    .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
-                    .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
-                    .setInterpolator(Interpolators.ALPHA_OUT)
-                    .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable)
-                    .start();
-        } else if (statusBarState == StatusBarState.KEYGUARD
-                || statusBarState == StatusBarState.SHADE_LOCKED) {
-            mKeyguardBottomArea.setVisibility(View.VISIBLE);
-            mKeyguardBottomArea.setAlpha(1f);
-        } else {
-            mKeyguardBottomArea.setVisibility(View.GONE);
-        }
-    }
-
-    private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
-            boolean goingToFullShade) {
-        mKeyguardStatusView.animate().cancel();
-        mKeyguardStatusViewAnimating = false;
-        if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
-                && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
-            mKeyguardStatusViewAnimating = true;
-            mKeyguardStatusView.animate()
-                    .alpha(0f)
-                    .setStartDelay(0)
-                    .setDuration(160)
-                    .setInterpolator(Interpolators.ALPHA_OUT)
-                    .withEndAction(mAnimateKeyguardStatusViewGoneEndRunnable);
-            if (keyguardFadingAway) {
-                mKeyguardStatusView.animate()
-                        .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay())
-                        .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration())
-                        .start();
-            }
-        } else if (mBarState == StatusBarState.SHADE_LOCKED
-                && statusBarState == StatusBarState.KEYGUARD) {
-            mKeyguardStatusView.setVisibility(View.VISIBLE);
-            mKeyguardStatusViewAnimating = true;
-            mKeyguardStatusView.setAlpha(0f);
-            mKeyguardStatusView.animate()
-                    .alpha(1f)
-                    .setStartDelay(0)
-                    .setDuration(320)
-                    .setInterpolator(Interpolators.ALPHA_IN)
-                    .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
-        } else if (statusBarState == StatusBarState.KEYGUARD) {
-            if (keyguardFadingAway) {
-                mKeyguardStatusViewAnimating = true;
-                mKeyguardStatusView.animate()
-                        .alpha(0)
-                        .translationYBy(-getHeight() * 0.05f)
-                        .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
-                        .setDuration(125)
-                        .setStartDelay(0)
-                        .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable)
-                        .start();
-            } else {
-                mKeyguardStatusView.setVisibility(View.VISIBLE);
-                mKeyguardStatusView.setAlpha(1f);
-            }
-        } else {
-            mKeyguardStatusView.setVisibility(View.GONE);
-            mKeyguardStatusView.setAlpha(1f);
-        }
-    }
-
-    private void updateQsState() {
-        mNotificationStackScroller.setQsExpanded(mQsExpanded);
-        mNotificationStackScroller.setScrollingEnabled(
-                mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
-                        || mQsExpansionFromOverscroll));
-        updateEmptyShadeView();
-        if (mNPVPluginManager != null) {
-            mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
-                    ? View.VISIBLE
-                    : View.INVISIBLE);
-        }
-        mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
-                && !mStackScrollerOverscrolling && mQsScrimEnabled
-                ? View.VISIBLE
-                : View.INVISIBLE);
-        if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
-            mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
-        }
-        if (mQs == null) return;
-        mQs.setExpanded(mQsExpanded);
-    }
-
-    private void setQsExpansion(float height) {
-        height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
-        mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
-        if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
-                && !mDozing) {
-            setQsExpanded(true);
-        } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
-            setQsExpanded(false);
-        }
-        mQsExpansionHeight = height;
-        updateQsExpansion();
-        requestScrollerTopPaddingUpdate(false /* animate */);
-        updateHeaderKeyguardAlpha();
-        if (mBarState == StatusBarState.SHADE_LOCKED
-                || mBarState == StatusBarState.KEYGUARD) {
-            updateKeyguardBottomAreaAlpha();
-            updateBigClockAlpha();
-        }
-        if (mBarState == StatusBarState.SHADE && mQsExpanded
-                && !mStackScrollerOverscrolling && mQsScrimEnabled) {
-            mQsNavbarScrim.setAlpha(getQsExpansionFraction());
-        }
-
-        if (mAccessibilityManager.isEnabled()) {
-            setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
-        }
-
-        if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
-                && mFalsingManager.shouldEnforceBouncer()) {
-            mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
-                    false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
-        }
-        for (int i = 0; i < mExpansionListeners.size(); i++) {
-            mExpansionListeners.get(i).onQsExpansionChanged(mQsMaxExpansionHeight != 0
-                    ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
-        }
-        if (DEBUG) {
-            invalidate();
-        }
-    }
-
-    protected void updateQsExpansion() {
-        if (mQs == null) return;
-        float qsExpansionFraction = getQsExpansionFraction();
-        mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
-        int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
-        if (mNPVPluginManager != null) {
-            mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
-        }
-        mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
-    }
-
-    private String determineAccessibilityPaneTitle() {
-        if (mQs != null && mQs.isCustomizing()) {
-            return getContext().getString(R.string.accessibility_desc_quick_settings_edit);
-        } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
-            // Upon initialisation when we are not layouted yet we don't want to announce that we
-            // are fully expanded, hence the != 0.0f check.
-            return getContext().getString(R.string.accessibility_desc_quick_settings);
-        } else if (mBarState == StatusBarState.KEYGUARD) {
-            return getContext().getString(R.string.accessibility_desc_lock_screen);
-        } else {
-            return getContext().getString(R.string.accessibility_desc_notification_shade);
-        }
-    }
-
-    private float calculateQsTopPadding() {
-        if (mKeyguardShowing
-                && (mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
-
-            // Either QS pushes the notifications down when fully expanded, or QS is fully above the
-            // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
-            // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
-            // panel. We need to take the maximum and linearly interpolate with the panel expansion
-            // for a nice motion.
-            int maxNotificationPadding = getKeyguardNotificationStaticPadding();
-            int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
-            int max = mBarState == StatusBarState.KEYGUARD
-                    ? Math.max(maxNotificationPadding, maxQsPadding)
-                    : maxQsPadding;
-            return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
-                    getExpandedFraction());
-        } else if (mQsSizeChangeAnimator != null) {
-            return Math.max((int) mQsSizeChangeAnimator.getAnimatedValue(),
-                    getKeyguardNotificationStaticPadding());
-        } else if (mKeyguardShowing) {
-            // We can only do the smoother transition on Keyguard when we also are not collapsing
-            // from a scrolled quick settings.
-            return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
-                    (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
-                    getQsExpansionFraction());
-        } else {
-            return mQsExpansionHeight + mQsNotificationTopPadding;
-        }
-    }
-
-    /**
-     * @return the topPadding of notifications when on keyguard not respecting quick settings
-     *         expansion
-     */
-    private int getKeyguardNotificationStaticPadding() {
-        if (!mKeyguardShowing) {
-            return 0;
-        }
-        if (!mKeyguardBypassController.getBypassEnabled()) {
-            return mClockPositionResult.stackScrollerPadding;
-        }
-        int collapsedPosition = mHeadsUpInset;
-        if (!mNotificationStackScroller.isPulseExpanding()) {
-            return collapsedPosition;
-        } else {
-            int expandedPosition = mClockPositionResult.stackScrollerPadding;
-            return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
-                    mNotificationStackScroller.calculateAppearFractionBypass());
-        }
-    }
-
-
-    protected void requestScrollerTopPaddingUpdate(boolean animate) {
-        mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
-        if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
-            // update the position of the header
-            updateQsExpansion();
-        }
-    }
-
-
-    private void updateQSPulseExpansion() {
-        if (mQs != null) {
-            mQs.setShowCollapsedOnKeyguard(mKeyguardShowing
-                    && mKeyguardBypassController.getBypassEnabled()
-                    && mNotificationStackScroller.isPulseExpanding());
-        }
-    }
-
-    private void trackMovement(MotionEvent event) {
-        if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
-        mLastTouchX = event.getX();
-        mLastTouchY = event.getY();
-    }
-
-    private void initVelocityTracker() {
-        if (mQsVelocityTracker != null) {
-            mQsVelocityTracker.recycle();
-        }
-        mQsVelocityTracker = VelocityTracker.obtain();
-    }
-
-    private float getCurrentQSVelocity() {
-        if (mQsVelocityTracker == null) {
-            return 0;
-        }
-        mQsVelocityTracker.computeCurrentVelocity(1000);
-        return mQsVelocityTracker.getYVelocity();
-    }
-
-    private void cancelQsAnimation() {
-        if (mQsExpansionAnimator != null) {
-            mQsExpansionAnimator.cancel();
-        }
-    }
-
-    /**
-     * @see #flingSettings(float, int, Runnable, boolean)
-     */
-    public void flingSettings(float vel, int type) {
-        flingSettings(vel, type, null, false /* isClick */);
-    }
-
-    /**
-     * Animates QS or QQS as if the user had swiped up or down.
-     *
-     * @param vel Finger velocity or 0 when not initiated by touch events.
-     * @param type Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link #FLING_HIDE}.
-     * @param onFinishRunnable Runnable to be executed at the end of animation.
-     * @param isClick If originated by click (different interpolator and duration.)
-     */
-    protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
-            boolean isClick) {
-        float target;
-        switch (type) {
-            case FLING_EXPAND:
-                target = mQsMaxExpansionHeight;
-                break;
-            case FLING_COLLAPSE:
-                target = mQsMinExpansionHeight;
-                break;
-            case FLING_HIDE:
-            default:
-                target = 0;
-        }
-        if (target == mQsExpansionHeight) {
-            if (onFinishRunnable != null) {
-                onFinishRunnable.run();
-            }
-            return;
-        }
-
-        // If we move in the opposite direction, reset velocity and use a different duration.
-        boolean oppositeDirection = false;
-        boolean expanding = type == FLING_EXPAND;
-        if (vel > 0 && !expanding || vel < 0 && expanding) {
-            vel = 0;
-            oppositeDirection = true;
-        }
-        ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
-        if (isClick) {
-            animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
-            animator.setDuration(368);
-        } else {
-            mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
-        }
-        if (oppositeDirection) {
-            animator.setDuration(350);
-        }
-        animator.addUpdateListener(animation -> {
-            setQsExpansion((Float) animation.getAnimatedValue());
-        });
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mNotificationStackScroller.resetCheckSnoozeLeavebehind();
-                mQsExpansionAnimator = null;
-                if (onFinishRunnable != null) {
-                    onFinishRunnable.run();
-                }
-            }
-        });
-        animator.start();
-        mQsExpansionAnimator = animator;
-        mQsAnimatorExpand = expanding;
-    }
-
-    /**
-     * @return Whether we should intercept a gesture to open Quick Settings.
-     */
-    private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
-        if (!mQsExpansionEnabled || mCollapsedOnDown
-                || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())) {
-            return false;
-        }
-        View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
-        final boolean onHeader = x >= mQsFrame.getX()
-                && x <= mQsFrame.getX() + mQsFrame.getWidth()
-                && y >= header.getTop() && y <= header.getBottom();
-        if (mQsExpanded) {
-            return onHeader || (yDiff < 0 && isInQsArea(x, y));
-        } else {
-            return onHeader;
-        }
-    }
-
-    @Override
-    protected boolean isScrolledToBottom() {
-        if (!isInSettings()) {
-            return mBarState == StatusBarState.KEYGUARD
-                    || mNotificationStackScroller.isScrolledToBottom();
-        } else {
-            return true;
-        }
-    }
-
-    @Override
-    protected int getMaxPanelHeight() {
-        if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
-            return getMaxPanelHeightBypass();
-        } else {
-            return getMaxPanelHeightNonBypass();
-        }
-    }
-
-    private int getMaxPanelHeightNonBypass() {
-        int min = mStatusBarMinHeight;
-        if (!(mBarState == StatusBarState.KEYGUARD)
-                && mNotificationStackScroller.getNotGoneChildCount() == 0) {
-            int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
-            min = Math.max(min, minHeight);
-        }
-        int maxHeight;
-        if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
-                || mPulsing) {
-            maxHeight = calculatePanelHeightQsExpanded();
-        } else {
-            maxHeight = calculatePanelHeightShade();
-        }
-        maxHeight = Math.max(maxHeight, min);
-        return maxHeight;
-    }
-
-    private int getMaxPanelHeightBypass() {
-        int position = mClockPositionAlgorithm.getExpandedClockPosition()
-                + mKeyguardStatusView.getHeight();
-        if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
-            position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
-        }
-        return position;
-    }
-
-    public boolean isInSettings() {
-        return mQsExpanded;
-    }
-
-    public boolean isExpanding() {
-        return mIsExpanding;
-    }
-
-    @Override
-    protected void onHeightUpdated(float expandedHeight) {
-        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
-            // Updating the clock position will set the top padding which might
-            // trigger a new panel height and re-position the clock.
-            // This is a circular dependency and should be avoided, otherwise we'll have
-            // a stack overflow.
-            if (mStackScrollerMeasuringPass > 2) {
-                if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
-            } else {
-                positionClockAndNotifications();
-            }
-        }
-        if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
-                && !mQsExpansionFromOverscroll) {
-            float t;
-            if (mKeyguardShowing) {
-
-                // On Keyguard, interpolate the QS expansion linearly to the panel expansion
-                t = expandedHeight / (getMaxPanelHeight());
-            } else {
-                // In Shade, interpolate linearly such that QS is closed whenever panel height is
-                // minimum QS expansion + minStackHeight
-                float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
-                        + mNotificationStackScroller.getLayoutMinHeight();
-                float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
-                t = (expandedHeight - panelHeightQsCollapsed)
-                        / (panelHeightQsExpanded - panelHeightQsCollapsed);
-            }
-            float targetHeight = mQsMinExpansionHeight
-                    + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
-            setQsExpansion(targetHeight);
-            mHomeControlsLayout.setTranslationY(targetHeight);
-        }
-        updateExpandedHeight(expandedHeight);
-        updateHeader();
-        updateNotificationTranslucency();
-        updatePanelExpanded();
-        updateGestureExclusionRect();
-        if (DEBUG) {
-            invalidate();
-        }
-    }
-
-    private void updatePanelExpanded() {
-        boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
-        if (mPanelExpanded != isExpanded) {
-            mHeadsUpManager.setIsPanelExpanded(isExpanded);
-            mStatusBar.setPanelExpanded(isExpanded);
-            mPanelExpanded = isExpanded;
-        }
-    }
-
-    private int calculatePanelHeightShade() {
-        int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
-        int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
-        maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
-
-        if (mBarState == StatusBarState.KEYGUARD) {
-            int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition()
-                    + mKeyguardStatusView.getHeight()
-                    + mNotificationStackScroller.getIntrinsicContentHeight();
-            return Math.max(maxHeight, minKeyguardPanelBottom);
-        } else {
-            return maxHeight;
-        }
-    }
-
-    private int calculatePanelHeightQsExpanded() {
-        float notificationHeight = mNotificationStackScroller.getHeight()
-                - mNotificationStackScroller.getEmptyBottomMargin()
-                - mNotificationStackScroller.getTopPadding();
-
-        // When only empty shade view is visible in QS collapsed state, simulate that we would have
-        // it in expanded QS state as well so we don't run into troubles when fading the view in/out
-        // and expanding/collapsing the whole panel from/to quick settings.
-        if (mNotificationStackScroller.getNotGoneChildCount() == 0
-                && mShowEmptyShadeView) {
-            notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
-        }
-        int maxQsHeight = mQsMaxExpansionHeight;
-
-        if (mKeyguardShowing) {
-            maxQsHeight += mQsNotificationTopPadding;
-        }
-
-        // If an animation is changing the size of the QS panel, take the animated value.
-        if (mQsSizeChangeAnimator != null) {
-            maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
-        }
-        float totalHeight = Math.max(
-                maxQsHeight, mBarState == StatusBarState.KEYGUARD
-                        ? mClockPositionResult.stackScrollerPadding : 0)
-                + notificationHeight + mNotificationStackScroller.getTopPaddingOverflow();
-        if (totalHeight > mNotificationStackScroller.getHeight()) {
-            float fullyCollapsedHeight = maxQsHeight
-                    + mNotificationStackScroller.getLayoutMinHeight();
-            totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
-        }
-        return (int) totalHeight;
-    }
-
-    private void updateNotificationTranslucency() {
-        float alpha = 1f;
-        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp &&
-                !mHeadsUpManager.hasPinnedHeadsUp()) {
-            alpha = getFadeoutAlpha();
-        }
-        if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
-                && !mKeyguardBypassController.getBypassEnabled()) {
-            alpha *= mClockPositionResult.clockAlpha;
-        }
-        mNotificationStackScroller.setAlpha(alpha);
-    }
-
-    private float getFadeoutAlpha() {
-        float alpha;
-        if (mQsMinExpansionHeight == 0) {
-            return 1.0f;
-        }
-        alpha = getExpandedHeight() / mQsMinExpansionHeight;
-        alpha = Math.max(0, Math.min(alpha, 1));
-        alpha = (float) Math.pow(alpha, 0.75);
-        return alpha;
-    }
-
-    @Override
-    protected float getOverExpansionAmount() {
-        return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
-    }
-
-    @Override
-    protected float getOverExpansionPixels() {
-        return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
-    }
-
-    /**
-     * Hides the header when notifications are colliding with it.
-     */
-    private void updateHeader() {
-        if (mBarState == StatusBarState.KEYGUARD) {
-            updateHeaderKeyguardAlpha();
-        }
-        updateQsExpansion();
-    }
-
-    protected float getHeaderTranslation() {
-        if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
-            return -mQs.getQsMinExpansionHeight();
-        }
-        float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
-        float startHeight = -mQsExpansionHeight;
-        if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
-                && mNotificationStackScroller.isPulseExpanding()) {
-            if (!mPulseExpansionHandler.isExpanding()
-                    && !mPulseExpansionHandler.getLeavingLockscreen()) {
-                // If we aborted the expansion we need to make sure the header doesn't reappear
-                // again after the header has animated away
-                appearAmount = 0;
-            } else {
-                appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
-            }
-            startHeight = -mQs.getQsMinExpansionHeight();
-            if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
-        }
-        float translation = MathUtils.lerp(startHeight, 0,
-                Math.min(1.0f, appearAmount))
-                + mExpandOffset;
-        return Math.min(0, translation);
-    }
-
-    /**
-     * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
-     *         during swiping up
-     */
-    private float getKeyguardContentsAlpha() {
-        float alpha;
-        if (mBarState == StatusBarState.KEYGUARD) {
-
-            // When on Keyguard, we hide the header as soon as we expanded close enough to the
-            // header
-            alpha = getExpandedHeight()
-                    /
-                    (mKeyguardStatusBar.getHeight() + mNotificationsHeaderCollideDistance);
-        } else {
-
-            // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
-            // soon as we start translating the stack.
-            alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
-        }
-        alpha = MathUtils.saturate(alpha);
-        alpha = (float) Math.pow(alpha, 0.75);
-        return alpha;
-    }
-
-    private void updateHeaderKeyguardAlpha() {
-        if (!mKeyguardShowing) {
-            return;
-        }
-        float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
-        float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
-                * mKeyguardStatusBarAnimateAlpha;
-        newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
-        mKeyguardStatusBar.setAlpha(newAlpha);
-        boolean hideForBypass = mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
-                || mDelayShowingKeyguardStatusBar;
-        mKeyguardStatusBar.setVisibility(newAlpha != 0f && !mDozing && !hideForBypass
-                ? VISIBLE : INVISIBLE);
-    }
-
-    private void updateKeyguardBottomAreaAlpha() {
-        // There are two possible panel expansion behaviors:
-        // • User dragging up to unlock: we want to fade out as quick as possible
-        //   (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
-        // • User tapping on lock screen: bouncer won't be visible but panel expansion will
-        //   change due to "unlock hint animation." In this case, fading out the bottom area
-        //   would also hide the message that says "swipe to unlock," we don't want to do that.
-        float expansionAlpha = MathUtils.map(isUnlockHintRunning()
-                        ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f,
-                0f, 1f, getExpandedFraction());
-        float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
-        alpha *= mBottomAreaShadeAlpha;
-        mKeyguardBottomArea.setAffordanceAlpha(alpha);
-        mKeyguardBottomArea.setImportantForAccessibility(alpha == 0f
-                ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
-                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-        View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
-        if (ambientIndicationContainer != null) {
-            ambientIndicationContainer.setAlpha(alpha);
-        }
-    }
-
-    /**
-     * Custom clock fades away when user drags up to unlock or pulls down quick settings.
-     *
-     * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
-     * {@link updateKeyguardBottomAreaAlpha}.
-     */
-    private void updateBigClockAlpha() {
-        float expansionAlpha = MathUtils.map(isUnlockHintRunning()
-                ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f, getExpandedFraction());
-        float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
-        mBigClockContainer.setAlpha(alpha);
-    }
-
-    @Override
-    protected void onExpandingStarted() {
-        super.onExpandingStarted();
-        mNotificationStackScroller.onExpansionStarted();
-        mIsExpanding = true;
-        mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
-        if (mQsExpanded) {
-            onQsExpansionStarted();
-        }
-        // Since there are QS tiles in the header now, we need to make sure we start listening
-        // immediately so they can be up to date.
-        if (mQs == null) return;
-        mQs.setHeaderListening(true);
-    }
-
-    @Override
-    protected void onExpandingFinished() {
-        super.onExpandingFinished();
-        mNotificationStackScroller.onExpansionStopped();
-        mHeadsUpManager.onExpandingFinished();
-        mIsExpanding = false;
-        if (isFullyCollapsed()) {
-            DejankUtils.postAfterTraversal(new Runnable() {
-                @Override
-                public void run() {
-                    setListening(false);
-                }
-            });
-
-            // Workaround b/22639032: Make sure we invalidate something because else RenderThread
-            // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
-            // ahead with rendering and we jank.
-            postOnAnimation(new Runnable() {
-                @Override
-                public void run() {
-                    getParent().invalidateChild(NotificationPanelView.this, mDummyDirtyRect);
-                }
-            });
-        } else {
-            setListening(true);
-        }
-        mQsExpandImmediate = false;
-        mNotificationStackScroller.setShouldShowShelfOnly(false);
-        mTwoFingerQsExpandPossible = false;
-        mIsExpansionFromHeadsUp = false;
-        notifyListenersTrackingHeadsUp(null);
-        mExpandingFromHeadsUp = false;
-        setPanelScrimMinFraction(0.0f);
-    }
-
-    private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
-        for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
-            Consumer<ExpandableNotificationRow> listener
-                    = mTrackingHeadsUpListeners.get(i);
-            listener.accept(pickedChild);
-        }
-    }
-
-    private void setListening(boolean listening) {
-        mKeyguardStatusBar.setListening(listening);
-        if (mQs == null) return;
-        mQs.setListening(listening);
-        if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
-    }
-
-    @Override
-    public void expand(boolean animate) {
-        super.expand(animate);
-        setListening(true);
-    }
-
-    @Override
-    protected void setOverExpansion(float overExpansion, boolean isPixels) {
-        if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
-            return;
-        }
-        if (mBarState != StatusBarState.KEYGUARD) {
-            mNotificationStackScroller.setOnHeightChangedListener(null);
-            if (isPixels) {
-                mNotificationStackScroller.setOverScrolledPixels(
-                        overExpansion, true /* onTop */, false /* animate */);
-            } else {
-                mNotificationStackScroller.setOverScrollAmount(
-                        overExpansion, true /* onTop */, false /* animate */);
-            }
-            mNotificationStackScroller.setOnHeightChangedListener(this);
-        }
-    }
-
-    @Override
-    protected void onTrackingStarted() {
-        mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
-        super.onTrackingStarted();
-        if (mQsFullyExpanded) {
-            mQsExpandImmediate = true;
-            mNotificationStackScroller.setShouldShowShelfOnly(true);
-        }
-        if (mBarState == StatusBarState.KEYGUARD
-                || mBarState == StatusBarState.SHADE_LOCKED) {
-            mAffordanceHelper.animateHideLeftRightIcon();
-        }
-        mNotificationStackScroller.onPanelTrackingStarted();
-    }
-
-    @Override
-    protected void onTrackingStopped(boolean expand) {
-        mFalsingManager.onTrackingStopped();
-        super.onTrackingStopped(expand);
-        if (expand) {
-            mNotificationStackScroller.setOverScrolledPixels(
-                    0.0f, true /* onTop */, true /* animate */);
-        }
-        mNotificationStackScroller.onPanelTrackingStopped();
-        if (expand && (mBarState == StatusBarState.KEYGUARD
-                || mBarState == StatusBarState.SHADE_LOCKED)) {
-            if (!mHintAnimationRunning) {
-                mAffordanceHelper.reset(true);
-            }
-        }
-    }
-
-    @Override
-    public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
-
-        // Block update if we are in quick settings and just the top padding changed
-        // (i.e. view == null).
-        if (view == null && mQsExpanded) {
-            return;
-        }
-        if (needsAnimation && mInterpolatedDarkAmount == 0) {
-            mAnimateNextPositionUpdate = true;
-        }
-        ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
-        ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow
-                ? (ExpandableNotificationRow) firstChildNotGone
-                : null;
-        if (firstRow != null
-                && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) {
-            requestScrollerTopPaddingUpdate(false /* animate */);
-        }
-        requestPanelHeightUpdate();
-    }
-
-    @Override
-    public void onReset(ExpandableView view) {
-    }
-
-    public void onQsHeightChanged() {
-        mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
-        if (mQsExpanded && mQsFullyExpanded) {
-            mQsExpansionHeight = mQsMaxExpansionHeight;
-            requestScrollerTopPaddingUpdate(false /* animate */);
-            requestPanelHeightUpdate();
-        }
-        if (mAccessibilityManager.isEnabled()) {
-            setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
-        }
-        mNotificationStackScroller.setMaxTopPadding(
-                mQsMaxExpansionHeight + mQsNotificationTopPadding);
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        mAffordanceHelper.onConfigurationChanged();
-        if (newConfig.orientation != mLastOrientation) {
-            resetHorizontalPanelPosition();
-        }
-        mLastOrientation = newConfig.orientation;
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mNavigationBarBottomHeight = insets.getStableInsetBottom();
-        updateMaxHeadsUpTranslation();
-        return insets;
-    }
-
-    private void updateMaxHeadsUpTranslation() {
-        mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
-    }
-
     @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         if (layoutDirection != mOldLayoutDirection) {
@@ -2681,328 +64,10 @@
     }
 
     @Override
-    public void onClick(View v) {
-        onQsExpansionStarted();
-        if (mQsExpanded) {
-            flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
-                    true /* isClick */);
-        } else if (mQsExpansionEnabled) {
-            mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
-            flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
-                    true /* isClick */);
-        }
-    }
-
-    @Override
-    public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
-        boolean start = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? rightPage : !rightPage;
-        mIsLaunchTransitionRunning = true;
-        mLaunchAnimationEndRunnable = null;
-        float displayDensity = mStatusBar.getDisplayDensity();
-        int lengthDp = Math.abs((int) (translation / displayDensity));
-        int velocityDp = Math.abs((int) (vel / displayDensity));
-        if (start) {
-            mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
-
-            mFalsingManager.onLeftAffordanceOn();
-            if (mFalsingManager.shouldEnforceBouncer()) {
-                mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
-                    @Override
-                    public void run() {
-                        mKeyguardBottomArea.launchLeftAffordance();
-                    }
-                }, null, true /* dismissShade */, false /* afterKeyguardGone */,
-                        true /* deferred */);
-            } else {
-                mKeyguardBottomArea.launchLeftAffordance();
-            }
-        } else {
-            if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
-                    mLastCameraLaunchSource)) {
-                mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
-            }
-            mFalsingManager.onCameraOn();
-            if (mFalsingManager.shouldEnforceBouncer()) {
-                mStatusBar.executeRunnableDismissingKeyguard(new Runnable() {
-                    @Override
-                    public void run() {
-                        mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
-                    }
-                }, null, true /* dismissShade */, false /* afterKeyguardGone */,
-                    true /* deferred */);
-            }
-            else {
-                mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
-            }
-        }
-        mStatusBar.startLaunchTransitionTimeout();
-        mBlockTouches = true;
-    }
-
-    @Override
-    public void onAnimationToSideEnded() {
-        mIsLaunchTransitionRunning = false;
-        mIsLaunchTransitionFinished = true;
-        if (mLaunchAnimationEndRunnable != null) {
-            mLaunchAnimationEndRunnable.run();
-            mLaunchAnimationEndRunnable = null;
-        }
-        mStatusBar.readyForKeyguardDone();
-    }
-
-    @Override
-    protected void startUnlockHintAnimation() {
-        if (mPowerManager.isPowerSaveMode()) {
-            onUnlockHintStarted();
-            onUnlockHintFinished();
-            return;
-        }
-        super.startUnlockHintAnimation();
-    }
-
-    @Override
-    public float getMaxTranslationDistance() {
-        return (float) Math.hypot(getWidth(), getHeight());
-    }
-
-    @Override
-    public void onSwipingStarted(boolean rightIcon) {
-        mFalsingManager.onAffordanceSwipingStarted(rightIcon);
-        boolean camera = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon
-                : rightIcon;
-        if (camera) {
-            mKeyguardBottomArea.bindCameraPrewarmService();
-        }
-        requestDisallowInterceptTouchEvent(true);
-        mOnlyAffordanceInThisMotion = true;
-        mQsTracking = false;
-    }
-
-    @Override
-    public void onSwipingAborted() {
-        mFalsingManager.onAffordanceSwipingAborted();
-        mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
-    }
-
-    @Override
-    public void onIconClicked(boolean rightIcon) {
-        if (mHintAnimationRunning) {
-            return;
-        }
-        mHintAnimationRunning = true;
-        mAffordanceHelper.startHintAnimation(rightIcon, new Runnable() {
-            @Override
-            public void run() {
-                mHintAnimationRunning = false;
-                mStatusBar.onHintFinished();
-            }
-        });
-        rightIcon = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? !rightIcon : rightIcon;
-        if (rightIcon) {
-            mStatusBar.onCameraHintStarted();
-        } else {
-            if (mKeyguardBottomArea.isLeftVoiceAssist()) {
-                mStatusBar.onVoiceAssistHintStarted();
-            } else {
-                mStatusBar.onPhoneHintStarted();
-            }
-        }
-    }
-
-    @Override
-    protected void onUnlockHintFinished() {
-        super.onUnlockHintFinished();
-        mNotificationStackScroller.setUnlockHintRunning(false);
-    }
-
-    @Override
-    protected void onUnlockHintStarted() {
-        super.onUnlockHintStarted();
-        mNotificationStackScroller.setUnlockHintRunning(true);
-    }
-
-    @Override
-    public KeyguardAffordanceView getLeftIcon() {
-        return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getRightView()
-                : mKeyguardBottomArea.getLeftView();
-    }
-
-    @Override
-    public KeyguardAffordanceView getRightIcon() {
-        return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getLeftView()
-                : mKeyguardBottomArea.getRightView();
-    }
-
-    @Override
-    public View getLeftPreview() {
-        return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getRightPreview()
-                : mKeyguardBottomArea.getLeftPreview();
-    }
-
-    @Override
-    public View getRightPreview() {
-        return getLayoutDirection() == LAYOUT_DIRECTION_RTL
-                ? mKeyguardBottomArea.getLeftPreview()
-                : mKeyguardBottomArea.getRightPreview();
-    }
-
-    @Override
-    public float getAffordanceFalsingFactor() {
-        return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
-    }
-
-    @Override
-    public boolean needsAntiFalsing() {
-        return mBarState == StatusBarState.KEYGUARD;
-    }
-
-    @Override
-    protected float getPeekHeight() {
-        if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
-            return mNotificationStackScroller.getPeekHeight();
-        } else {
-            return mQsMinExpansionHeight;
-        }
-    }
-
-    @Override
-    protected boolean shouldUseDismissingAnimation() {
-        return mBarState != StatusBarState.SHADE
-                && (mKeyguardStateController.canDismissLockScreen() || !isTracking());
-    }
-
-    @Override
-    protected boolean fullyExpandedClearAllVisible() {
-        return mNotificationStackScroller.isFooterViewNotGone()
-                && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
-    }
-
-    @Override
-    protected boolean isClearAllVisible() {
-        return mNotificationStackScroller.isFooterViewContentVisible();
-    }
-
-    @Override
-    protected int getClearAllHeight() {
-        return mNotificationStackScroller.getFooterViewHeight();
-    }
-
-    @Override
-    protected boolean isTrackingBlocked() {
-        return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
-    }
-
-    public boolean isQsExpanded() {
-        return mQsExpanded;
-    }
-
-    public boolean isQsDetailShowing() {
-        return mQs.isShowingDetail();
-    }
-
-    public void closeQsDetail() {
-        mQs.closeDetail();
-    }
-
-    @Override
     public boolean shouldDelayChildPressedState() {
         return true;
     }
 
-    public boolean isLaunchTransitionFinished() {
-        return mIsLaunchTransitionFinished;
-    }
-
-    public boolean isLaunchTransitionRunning() {
-        return mIsLaunchTransitionRunning;
-    }
-
-    public void setLaunchTransitionEndRunnable(Runnable r) {
-        mLaunchAnimationEndRunnable = r;
-    }
-
-    public void setEmptyDragAmount(float amount) {
-        mEmptyDragAmount = amount * 0.2f;
-        positionClockAndNotifications();
-    }
-
-    private void updateDozingVisibilities(boolean animate) {
-        mKeyguardBottomArea.setDozing(mDozing, animate);
-        if (!mDozing && animate) {
-            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        }
-    }
-
-    @Override
-    public boolean isDozing() {
-        return mDozing;
-    }
-
-    public void showEmptyShadeView(boolean emptyShadeViewVisible) {
-        mShowEmptyShadeView = emptyShadeViewVisible;
-        updateEmptyShadeView();
-    }
-
-    private void updateEmptyShadeView() {
-        // Hide "No notifications" in QS.
-        mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
-    }
-
-    public void setQsScrimEnabled(boolean qsScrimEnabled) {
-        boolean changed = mQsScrimEnabled != qsScrimEnabled;
-        mQsScrimEnabled = qsScrimEnabled;
-        if (changed) {
-            updateQsState();
-        }
-    }
-
-    public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
-        mKeyguardUserSwitcher = keyguardUserSwitcher;
-    }
-
-    public void onScreenTurningOn() {
-        mKeyguardStatusView.dozeTimeTick();
-    }
-
-    @Override
-    public void onEmptySpaceClicked(float x, float y) {
-        onEmptySpaceClick(x);
-    }
-
-    @Override
-    protected boolean onMiddleClicked() {
-        switch (mBarState) {
-            case StatusBarState.KEYGUARD:
-                if (!mDozingOnDown) {
-                    if (mKeyguardBypassController.getBypassEnabled()) {
-                        mUpdateMonitor.requestFaceAuth();
-                    } else {
-                        mLockscreenGestureLogger.write(
-                                MetricsEvent.ACTION_LS_HINT,
-                                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
-                        startUnlockHintAnimation();
-                    }
-                }
-                return true;
-            case StatusBarState.SHADE_LOCKED:
-                if (!mQsExpanded) {
-                    mStatusBarStateController.setState(StatusBarState.KEYGUARD);
-                }
-                return true;
-            case StatusBarState.SHADE:
-
-                // This gets called in the middle of the touch handling, where the state is still
-                // that we are tracking the panel. Collapse the panel after this is done.
-                post(mPostCollapseRunnable);
-                return false;
-            default:
-                return true;
-        }
-    }
-
     @Override
     protected void dispatchDraw(Canvas canvas) {
         super.dispatchDraw(canvas);
@@ -3011,250 +76,18 @@
         }
     }
 
-    public float getCurrentPanelAlpha() {
+    float getCurrentPanelAlpha() {
         return mCurrentPanelAlpha;
     }
 
-    public boolean setPanelAlpha(int alpha, boolean animate) {
-        if (mPanelAlpha != alpha) {
-            mPanelAlpha = alpha;
-            PropertyAnimator.setProperty(this, PANEL_ALPHA, alpha,
-                    alpha == 255 ? PANEL_ALPHA_IN_PROPERTIES : PANEL_ALPHA_OUT_PROPERTIES, animate);
-            return true;
-        }
-        return false;
-    }
-
-    public void setPanelAlphaInternal(float alpha) {
+    void setPanelAlphaInternal(float alpha) {
         mCurrentPanelAlpha = (int) alpha;
         mAlphaPaint.setARGB(mCurrentPanelAlpha, 255, 255, 255);
         invalidate();
     }
 
-    public void setPanelAlphaEndAction(Runnable r) {
-        mPanelAlphaEndAction = r;
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        if (DEBUG) {
-            Paint p = new Paint();
-            p.setColor(Color.RED);
-            p.setStrokeWidth(2);
-            p.setStyle(Paint.Style.STROKE);
-            canvas.drawLine(0, getMaxPanelHeight(), getWidth(), getMaxPanelHeight(), p);
-            p.setColor(Color.BLUE);
-            canvas.drawLine(0, getExpandedHeight(), getWidth(), getExpandedHeight(), p);
-            p.setColor(Color.GREEN);
-            canvas.drawLine(0, calculatePanelHeightQsExpanded(), getWidth(),
-                    calculatePanelHeightQsExpanded(), p);
-            p.setColor(Color.YELLOW);
-            canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
-                    calculatePanelHeightShade(), p);
-            p.setColor(Color.MAGENTA);
-            canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
-                    calculateQsTopPadding(), p);
-            p.setColor(Color.CYAN);
-            canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, getWidth(),
-                    mNotificationStackScroller.getTopPadding(), p);
-            p.setColor(Color.GRAY);
-            canvas.drawLine(0, mClockPositionResult.clockY, getWidth(),
-                    mClockPositionResult.clockY, p);
-        }
-    }
-
-    @Override
-    public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
-        mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
-        if (inPinnedMode) {
-            mHeadsUpExistenceChangedRunnable.run();
-            updateNotificationTranslucency();
-        } else {
-            setHeadsUpAnimatingAway(true);
-            mNotificationStackScroller.runAfterAnimationFinished(
-                    mHeadsUpExistenceChangedRunnable);
-        }
-        updateGestureExclusionRect();
-        mHeadsUpPinnedMode = inPinnedMode;
-        updateHeadsUpVisibility();
-        updateKeyguardStatusBarForHeadsUp();
-    }
-
-    private void updateKeyguardStatusBarForHeadsUp() {
-        boolean showingKeyguardHeadsUp = mKeyguardShowing
-                && mHeadsUpAppearanceController.shouldBeVisible();
-        if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
-            mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
-            if (mKeyguardShowing) {
-                PropertyAnimator.setProperty(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
-                        showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
-                        true /* animate */);
-            } else {
-                PropertyAnimator.applyImmediately(this, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
-            }
-        }
-    }
-
-    private void setKeyguardHeadsUpShowingAmount(float amount) {
-        mKeyguardHeadsUpShowingAmount = amount;
-        updateHeaderKeyguardAlpha();
-    }
-
-    private float getKeyguardHeadsUpShowingAmount() {
-        return mKeyguardHeadsUpShowingAmount;
-    }
-
-    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
-        mHeadsUpAnimatingAway = headsUpAnimatingAway;
-        mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
-        updateHeadsUpVisibility();
-    }
-
-    private void updateHeadsUpVisibility() {
-        ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
-    }
-
-    @Override
-    public void onHeadsUpPinned(NotificationEntry entry) {
-        if (!isOnKeyguard()) {
-            mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
-                    true);
-        }
-    }
-
-    @Override
-    public void onHeadsUpUnPinned(NotificationEntry entry) {
-
-        // When we're unpinning the notification via active edge they remain heads-upped,
-        // we need to make sure that an animation happens in this case, otherwise the notification
-        // will stick to the top without any interaction.
-        if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
-            mNotificationStackScroller.generateHeadsUpAnimation(
-                    entry.getHeadsUpAnimationView(), false);
-            entry.setHeadsUpIsVisible();
-        }
-    }
-
-    @Override
-    public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
-        mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
-    }
-
-    @Override
-    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
-        super.setHeadsUpManager(headsUpManager);
-        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
-                mNotificationStackScroller.getHeadsUpCallback(), this);
-    }
-
-    public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
-        if (pickedChild != null) {
-            notifyListenersTrackingHeadsUp(pickedChild);
-            mExpandingFromHeadsUp = true;
-        }
-        // otherwise we update the state when the expansion is finished
-    }
-
-    @Override
-    protected void onClosingFinished() {
-        super.onClosingFinished();
-        resetHorizontalPanelPosition();
-        setClosingWithAlphaFadeout(false);
-    }
-
-    private void setClosingWithAlphaFadeout(boolean closing) {
-        mClosingWithAlphaFadeOut = closing;
-        mNotificationStackScroller.forceNoOverlappingRendering(closing);
-    }
-
-    /**
-     * Updates the vertical position of the panel so it is positioned closer to the touch
-     * responsible for opening the panel.
-     *
-     * @param x the x-coordinate the touch event
-     */
-    protected void updateVerticalPanelPosition(float x) {
-        if (mNotificationStackScroller.getWidth() * 1.75f > getWidth()) {
-            resetHorizontalPanelPosition();
-            return;
-        }
-        float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
-        float rightMost = getWidth() - mPositionMinSideMargin
-                - mNotificationStackScroller.getWidth() / 2;
-        if (Math.abs(x - getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
-            x = getWidth() / 2;
-        }
-        x = Math.min(rightMost, Math.max(leftMost, x));
-        float center =
-                mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
-        setHorizontalPanelTranslation(x - center);
-    }
-
-    private void resetHorizontalPanelPosition() {
-        setHorizontalPanelTranslation(0f);
-    }
-
-    protected void setHorizontalPanelTranslation(float translation) {
-        mNotificationStackScroller.setTranslationX(translation);
-        mQsFrame.setTranslationX(translation);
-        int size = mVerticalTranslationListener.size();
-        for (int i = 0; i < size; i++) {
-            mVerticalTranslationListener.get(i).run();
-        }
-    }
-
-    protected void updateExpandedHeight(float expandedHeight) {
-        if (mTracking) {
-            mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
-        }
-        if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
-            // The expandedHeight is always the full panel Height when bypassing
-            expandedHeight = getMaxPanelHeightNonBypass();
-        }
-        mNotificationStackScroller.setExpandedHeight(expandedHeight);
-        updateKeyguardBottomAreaAlpha();
-        updateBigClockAlpha();
-        updateStatusBarIcons();
-    }
-
-    /**
-     * @return whether the notifications are displayed full width and don't have any margins on
-     *         the side.
-     */
-    public boolean isFullWidth() {
-        return mIsFullWidth;
-    }
-
-    private void updateStatusBarIcons() {
-        boolean showIconsWhenExpanded = (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
-                && getExpandedHeight() < getOpeningHeight();
-        if (showIconsWhenExpanded && mNoVisibleNotifications && isOnKeyguard()) {
-            showIconsWhenExpanded = false;
-        }
-        if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
-            mShowIconsWhenExpanded = showIconsWhenExpanded;
-            mCommandQueue.recomputeDisableFlags(mDisplayId, false);
-        }
-    }
-
-    private boolean isOnKeyguard() {
-        return mBarState == StatusBarState.KEYGUARD;
-    }
-
-    public void setPanelScrimMinFraction(float minFraction) {
-        mBar.panelScrimMinFractionChanged(minFraction);
-    }
-
-    public void clearNotificationEffects() {
-        mStatusBar.clearNotificationEffects();
-    }
-
-    @Override
-    protected boolean isPanelVisibleBecauseOfHeadsUp() {
-        return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
-                && mBarState == StatusBarState.SHADE;
+    public void setDozing(boolean dozing) {
+        mDozing = dozing;
     }
 
     @Override
@@ -3262,382 +95,4 @@
         return !mDozing;
     }
 
-    public void launchCamera(boolean animate, int source) {
-        if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
-            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
-        } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
-            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
-        } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
-            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
-        } else {
-
-            // Default.
-            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
-        }
-
-        // If we are launching it when we are occluded already we don't want it to animate,
-        // nor setting these flags, since the occluded state doesn't change anymore, hence it's
-        // never reset.
-        if (!isFullyCollapsed()) {
-            setLaunchingAffordance(true);
-        } else {
-            animate = false;
-        }
-        mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
-        mAffordanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
-    }
-
-    public void onAffordanceLaunchEnded() {
-        setLaunchingAffordance(false);
-    }
-
-    /**
-     * Set whether we are currently launching an affordance. This is currently only set when
-     * launched via a camera gesture.
-     */
-    private void setLaunchingAffordance(boolean launchingAffordance) {
-        mLaunchingAffordance = launchingAffordance;
-        getLeftIcon().setLaunchingAffordance(launchingAffordance);
-        getRightIcon().setLaunchingAffordance(launchingAffordance);
-        mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
-        if (mAffordanceLaunchListener != null) {
-            mAffordanceLaunchListener.accept(launchingAffordance);
-        }
-    }
-
-    /**
-     * Return true when a bottom affordance is launching an occluded activity with a splash screen.
-     */
-    public boolean isLaunchingAffordanceWithPreview() {
-        return mLaunchingAffordance && mAffordanceHasPreview;
-    }
-
-    /**
-     * Whether the camera application can be launched for the camera launch gesture.
-     */
-    public boolean canCameraGestureBeLaunched() {
-        if (!mStatusBar.isCameraAllowedByAdmin()) {
-            return false;
-        }
-
-        ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
-        String packageToLaunch = (resolveInfo == null || resolveInfo.activityInfo == null)
-                ? null : resolveInfo.activityInfo.packageName;
-        return packageToLaunch != null &&
-                (mBarState != StatusBarState.SHADE || !isForegroundApp(packageToLaunch))
-                && !mAffordanceHelper.isSwipingInProgress();
-    }
-
-    /**
-     * Return true if the applications with the package name is running in foreground.
-     *
-     * @param pkgName application package name.
-     */
-    private boolean isForegroundApp(String pkgName) {
-        ActivityManager am = getContext().getSystemService(ActivityManager.class);
-        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
-        return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
-    }
-
-    private void setGroupManager(NotificationGroupManager groupManager) {
-        mGroupManager = groupManager;
-    }
-
-    public boolean hideStatusBarIconsWhenExpanded() {
-        if (mLaunchingNotification) {
-            return mHideIconsDuringNotificationLaunch;
-        }
-        if (mHeadsUpAppearanceController != null
-                && mHeadsUpAppearanceController.shouldBeVisible()) {
-            return false;
-        }
-        return !isFullWidth() || !mShowIconsWhenExpanded;
-    }
-
-    private final FragmentListener mFragmentListener = new FragmentListener() {
-        @Override
-        public void onFragmentViewCreated(String tag, Fragment fragment) {
-            mQs = (QS) fragment;
-            mQs.setPanelView(NotificationPanelView.this);
-            mQs.setExpandClickListener(NotificationPanelView.this);
-            mQs.setHeaderClickable(mQsExpansionEnabled);
-            updateQSPulseExpansion();
-            mQs.setOverscrolling(mStackScrollerOverscrolling);
-
-            // recompute internal state when qspanel height changes
-            mQs.getView().addOnLayoutChangeListener(
-                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
-                        final int height = bottom - top;
-                        final int oldHeight = oldBottom - oldTop;
-                        if (height != oldHeight) {
-                            onQsHeightChanged();
-                        }
-                    });
-            mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
-            if (mQs instanceof QSFragment) {
-                mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
-            }
-            updateQsExpansion();
-        }
-
-        @Override
-        public void onFragmentViewDestroyed(String tag, Fragment fragment) {
-            // Manual handling of fragment lifecycle is only required because this bridges
-            // non-fragment and fragment code. Once we are using a fragment for the notification
-            // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
-            if (fragment == mQs) {
-                mQs = null;
-            }
-        }
-    };
-
-    @Override
-    public void setTouchAndAnimationDisabled(boolean disabled) {
-        super.setTouchAndAnimationDisabled(disabled);
-        if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
-            mAffordanceHelper.reset(false /* animate */);
-        }
-        mNotificationStackScroller.setAnimationsEnabled(!disabled);
-    }
-
-    /**
-     * Sets the dozing state.
-     *
-     * @param dozing {@code true} when dozing.
-     * @param animate if transition should be animated.
-     * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
-     */
-    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
-        if (dozing == mDozing) return;
-        mDozing = dozing;
-        mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
-        mKeyguardBottomArea.setDozing(mDozing, animate);
-
-        if (dozing) {
-            mBottomAreaShadeAlphaAnimator.cancel();
-        }
-
-        if (mBarState == StatusBarState.KEYGUARD
-                || mBarState == StatusBarState.SHADE_LOCKED) {
-            updateDozingVisibilities(animate);
-        }
-
-        final float dozeAmount = dozing ? 1 : 0;
-        mStatusBarStateController.setDozeAmount(dozeAmount, animate);
-    }
-
-    @Override
-    public void onDozeAmountChanged(float linearAmount, float amount) {
-        mInterpolatedDarkAmount = amount;
-        mLinearDarkAmount = linearAmount;
-        mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
-        mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
-        positionClockAndNotifications();
-    }
-
-    public void setPulsing(boolean pulsing) {
-        mPulsing = pulsing;
-        final boolean animatePulse = !mDozeParameters.getDisplayNeedsBlanking()
-                && mDozeParameters.getAlwaysOn();
-        if (animatePulse) {
-            mAnimateNextPositionUpdate = true;
-        }
-        // Do not animate the clock when waking up from a pulse.
-        // The height callback will take care of pushing the clock to the right position.
-        if (!mPulsing && !mDozing) {
-            mAnimateNextPositionUpdate = false;
-        }
-        mNotificationStackScroller.setPulsing(pulsing, animatePulse);
-        mKeyguardStatusView.setPulsing(pulsing);
-    }
-
-    public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
-        if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
-            mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
-            mStatusBar.updateKeyguardMaxNotifications();
-        }
-    }
-
-    public void dozeTimeTick() {
-        mKeyguardBottomArea.dozeTimeTick();
-        mKeyguardStatusView.dozeTimeTick();
-        if (mInterpolatedDarkAmount > 0) {
-            positionClockAndNotifications();
-        }
-    }
-
-    public void setStatusAccessibilityImportance(int mode) {
-        mKeyguardStatusView.setImportantForAccessibility(mode);
-    }
-
-    /**
-     * TODO: this should be removed.
-     * It's not correct to pass this view forward because other classes will end up adding
-     * children to it. Theme will be out of sync.
-     *
-     * @return bottom area view
-     */
-    public KeyguardBottomAreaView getKeyguardBottomAreaView() {
-        return mKeyguardBottomArea;
-    }
-
-    public void setUserSetupComplete(boolean userSetupComplete) {
-        mUserSetupComplete = userSetupComplete;
-        mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
-    }
-
-    public void applyExpandAnimationParams(ExpandAnimationParameters params) {
-        mExpandOffset = params != null ? params.getTopChange() : 0;
-        updateQsExpansion();
-        if (params != null) {
-            boolean hideIcons = params.getProgress(
-                    ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
-            if (hideIcons != mHideIconsDuringNotificationLaunch) {
-                mHideIconsDuringNotificationLaunch = hideIcons;
-                if (!hideIcons) {
-                    mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
-                }
-            }
-        }
-    }
-
-    public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
-        mTrackingHeadsUpListeners.add(listener);
-    }
-
-    public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
-        mTrackingHeadsUpListeners.remove(listener);
-    }
-
-    public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
-        mVerticalTranslationListener.add(verticalTranslationListener);
-    }
-
-    public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
-        mVerticalTranslationListener.remove(verticalTranslationListener);
-    }
-
-    public void setHeadsUpAppearanceController(
-            HeadsUpAppearanceController headsUpAppearanceController) {
-        mHeadsUpAppearanceController = headsUpAppearanceController;
-    }
-
-    /**
-     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
-     * security view of the bouncer.
-     */
-    public void onBouncerPreHideAnimation() {
-        setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
-                false /* goingToFullShade */);
-    }
-
-    /**
-     * Do not let the user drag the shade up and down for the current touch session.
-     * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
-     */
-    public void blockExpansionForCurrentTouch() {
-        mBlockingExpansionForCurrentTouch = mTracking;
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        super.dump(fd, pw, args);
-        pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect());
-        if (mKeyguardStatusBar != null) {
-            mKeyguardStatusBar.dump(fd, pw, args);
-        }
-        if (mKeyguardStatusView != null) {
-            mKeyguardStatusView.dump(fd, pw, args);
-        }
-    }
-
-    public boolean hasActiveClearableNotifications() {
-        return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
-    }
-
-    @Override
-    public void onZenChanged(int zen) {
-        updateShowEmptyShadeView();
-    }
-
-    private void updateShowEmptyShadeView() {
-        boolean showEmptyShadeView =
-                mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
-        showEmptyShadeView(showEmptyShadeView);
-    }
-
-    public RemoteInputController.Delegate createRemoteInputDelegate() {
-        return mNotificationStackScroller.createDelegate();
-    }
-
-    public void updateNotificationViews() {
-        mNotificationStackScroller.updateSectionBoundaries();
-        mNotificationStackScroller.updateSpeedBumpIndex();
-        mNotificationStackScroller.updateFooter();
-        updateShowEmptyShadeView();
-        mNotificationStackScroller.updateIconAreaViews();
-    }
-
-    public void onUpdateRowStates() {
-        mNotificationStackScroller.onUpdateRowStates();
-    }
-
-    public boolean hasPulsingNotifications() {
-        return mNotificationStackScroller.hasPulsingNotifications();
-    }
-
-    public ActivatableNotificationView getActivatedChild() {
-        return mNotificationStackScroller.getActivatedChild();
-    }
-
-    public void setActivatedChild(ActivatableNotificationView o) {
-        mNotificationStackScroller.setActivatedChild(o);
-    }
-
-    public void runAfterAnimationFinished(Runnable r) {
-        mNotificationStackScroller.runAfterAnimationFinished(r);
-    }
-
-    public void setScrollingEnabled(boolean b) {
-        mNotificationStackScroller.setScrollingEnabled(b);
-    }
-
-    public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
-            NotificationShelf notificationShelf,
-            HeadsUpManagerPhone headsUpManager,
-            NotificationIconAreaController notificationIconAreaController,
-            ScrimController scrimController) {
-        setStatusBar(statusBar);
-        setGroupManager(mGroupManager);
-        mNotificationStackScroller.setNotificationPanel(this);
-        mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
-        mNotificationStackScroller.setStatusBar(statusBar);
-        mNotificationStackScroller.setGroupManager(groupManager);
-        mNotificationStackScroller.setShelf(notificationShelf);
-        mNotificationStackScroller.setScrimController(scrimController);
-        updateShowEmptyShadeView();
-    }
-
-    public void showTransientIndication(int id) {
-        mKeyguardIndicationController.showTransientIndication(id);
-    }
-
-    @Override
-    public void onDynamicPrivacyChanged() {
-        // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
-        // of sync with the notification panel.
-        if (mLinearDarkAmount != 0) {
-            return;
-        }
-        mAnimateNextPositionUpdate = true;
-    }
-
-    public void setOnReinflationListener(Runnable onReinflationListener) {
-        mOnReinflationListener = onReinflationListener;
-    }
-
-    public static boolean isQsSplitEnabled() {
-        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
new file mode 100644
index 0000000..bbde25b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -0,0 +1,3733 @@
+/*
+ * 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.phone;
+
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.Fragment;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.util.Log;
+import android.util.MathUtils;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.KeyguardClockSwitch;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.HomeControlsPlugin;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyguardAffordanceView;
+import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationShelf;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.notification.AnimatableProperty;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.Utils;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import javax.inject.Inject;
+
+@StatusBarComponent.StatusBarScope
+public class NotificationPanelViewController extends PanelViewController {
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * Fling expanding QS.
+     */
+    private static final int FLING_EXPAND = 0;
+
+    /**
+     * Fling collapsing QS, potentially stopping when QS becomes QQS.
+     */
+    private static final int FLING_COLLAPSE = 1;
+
+    /**
+     * Fling until QS is completely hidden.
+     */
+    private static final int FLING_HIDE = 2;
+    private final DozeParameters mDozeParameters;
+    private final OnHeightChangedListener mOnHeightChangedListener = new OnHeightChangedListener();
+    private final OnClickListener mOnClickListener = new OnClickListener();
+    private final OnOverscrollTopChangedListener
+            mOnOverscrollTopChangedListener =
+            new OnOverscrollTopChangedListener();
+    private final KeyguardAffordanceHelperCallback
+            mKeyguardAffordanceHelperCallback =
+            new KeyguardAffordanceHelperCallback();
+    private final OnEmptySpaceClickListener
+            mOnEmptySpaceClickListener =
+            new OnEmptySpaceClickListener();
+    private final MyOnHeadsUpChangedListener
+            mOnHeadsUpChangedListener =
+            new MyOnHeadsUpChangedListener();
+    private final HeightListener mHeightListener = new HeightListener();
+    private final ZenModeControllerCallback
+            mZenModeControllerCallback =
+            new ZenModeControllerCallback();
+    private final ConfigurationListener mConfigurationListener = new ConfigurationListener();
+    private final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener();
+    private final ExpansionCallback mExpansionCallback = new ExpansionCallback();
+    private final NotificationPanelView mView;
+    private final MetricsLogger mMetricsLogger;
+    private final ActivityManager mActivityManager;
+    private final ZenModeController mZenModeController;
+    private final ConfigurationController mConfigurationController;
+    private final FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+
+    private double mQqsSplitFraction;
+
+    // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
+    // changed.
+    private static final int CAP_HEIGHT = 1456;
+    private static final int FONT_HEIGHT = 2163;
+
+    /**
+     * Maximum time before which we will expand the panel even for slow motions when getting a
+     * touch passed over from launcher.
+     */
+    private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300;
+
+    private static final String COUNTER_PANEL_OPEN = "panel_open";
+    private static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
+    private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
+
+    private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
+    private static final Rect EMPTY_RECT = new Rect();
+
+    private static final AnimationProperties
+            CLOCK_ANIMATION_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+    private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from(
+            "KEYGUARD_HEADS_UP_SHOWING_AMOUNT",
+            (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat),
+            (Function<NotificationPanelView, Float>) notificationPanelView ->
+                    getKeyguardHeadsUpShowingAmount(),
+            R.id.keyguard_hun_animator_tag, R.id.keyguard_hun_animator_end_tag,
+            R.id.keyguard_hun_animator_start_tag);
+    private static final AnimationProperties
+            KEYGUARD_HUN_PROPERTIES =
+            new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+    @VisibleForTesting
+    final KeyguardUpdateMonitorCallback
+            mKeyguardUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+
+                @Override
+                public void onBiometricAuthenticated(int userId,
+                        BiometricSourceType biometricSourceType) {
+                    if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+                        mDelayShowingKeyguardStatusBar = true;
+                    }
+                }
+
+                @Override
+                public void onBiometricRunningStateChanged(boolean running,
+                        BiometricSourceType biometricSourceType) {
+                    boolean
+                            keyguardOrShadeLocked =
+                            mBarState == StatusBarState.KEYGUARD
+                                    || mBarState == StatusBarState.SHADE_LOCKED;
+                    if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
+                            && !mDelayShowingKeyguardStatusBar) {
+                        mFirstBypassAttempt = false;
+                        animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                    }
+                }
+
+                @Override
+                public void onFinishedGoingToSleep(int why) {
+                    mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+                    mDelayShowingKeyguardStatusBar = false;
+                }
+            };
+
+    private final InjectionInflationController mInjectionInflationController;
+    private final PowerManager mPowerManager;
+    private final AccessibilityManager mAccessibilityManager;
+    private final NotificationWakeUpCoordinator mWakeUpCoordinator;
+    private final PulseExpansionHandler mPulseExpansionHandler;
+    private final KeyguardBypassController mKeyguardBypassController;
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+
+    private KeyguardAffordanceHelper mAffordanceHelper;
+    private KeyguardUserSwitcher mKeyguardUserSwitcher;
+    private KeyguardStatusBarView mKeyguardStatusBar;
+    private ViewGroup mBigClockContainer;
+    private QS mQs;
+    private FrameLayout mQsFrame;
+    private KeyguardStatusView mKeyguardStatusView;
+    private View mQsNavbarScrim;
+    private NotificationsQuickSettingsContainer mNotificationContainerParent;
+    private NotificationStackScrollLayout mNotificationStackScroller;
+    private FrameLayout mHomeControlsLayout;
+    private boolean mAnimateNextPositionUpdate;
+
+    private int mTrackingPointer;
+    private VelocityTracker mQsVelocityTracker;
+    private boolean mQsTracking;
+
+    /**
+     * If set, the ongoing touch gesture might both trigger the expansion in {@link PanelView} and
+     * the expansion for quick settings.
+     */
+    private boolean mConflictingQsExpansionGesture;
+
+    private boolean mPanelExpanded;
+    private boolean mQsExpanded;
+    private boolean mQsExpandedWhenExpandingStarted;
+    private boolean mQsFullyExpanded;
+    private boolean mKeyguardShowing;
+    private boolean mDozing;
+    private boolean mDozingOnDown;
+    private int mBarState;
+    private float mInitialHeightOnTouch;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private float mQsExpansionHeight;
+    private int mQsMinExpansionHeight;
+    private int mQsMaxExpansionHeight;
+    private int mQsPeekHeight;
+    private boolean mStackScrollerOverscrolling;
+    private boolean mQsExpansionFromOverscroll;
+    private float mLastOverscroll;
+    private boolean mQsExpansionEnabled = true;
+    private ValueAnimator mQsExpansionAnimator;
+    private FlingAnimationUtils mFlingAnimationUtils;
+    private int mStatusBarMinHeight;
+    private int mNotificationsHeaderCollideDistance;
+    private float mEmptyDragAmount;
+    private float mDownX;
+    private float mDownY;
+
+    private final KeyguardClockPositionAlgorithm
+            mClockPositionAlgorithm =
+            new KeyguardClockPositionAlgorithm();
+    private final KeyguardClockPositionAlgorithm.Result
+            mClockPositionResult =
+            new KeyguardClockPositionAlgorithm.Result();
+    private boolean mIsExpanding;
+
+    private boolean mBlockTouches;
+    // Used for two finger gesture as well as accessibility shortcut to QS.
+    private boolean mQsExpandImmediate;
+    private boolean mTwoFingerQsExpandPossible;
+
+    /**
+     * If we are in a panel collapsing motion, we reset scrollY of our scroll view but still
+     * need to take this into account in our panel height calculation.
+     */
+    private boolean mQsAnimatorExpand;
+    private boolean mIsLaunchTransitionFinished;
+    private boolean mIsLaunchTransitionRunning;
+    private Runnable mLaunchAnimationEndRunnable;
+    private boolean mOnlyAffordanceInThisMotion;
+    private boolean mKeyguardStatusViewAnimating;
+    private ValueAnimator mQsSizeChangeAnimator;
+
+    private boolean mShowEmptyShadeView;
+
+    private boolean mQsScrimEnabled = true;
+    private boolean mQsTouchAboveFalsingThreshold;
+    private int mQsFalsingThreshold;
+
+    private float mKeyguardStatusBarAnimateAlpha = 1f;
+    private HeadsUpTouchHelper mHeadsUpTouchHelper;
+    private boolean mListenForHeadsUp;
+    private int mNavigationBarBottomHeight;
+    private boolean mExpandingFromHeadsUp;
+    private boolean mCollapsedOnDown;
+    private int mPositionMinSideMargin;
+    private int mLastOrientation = -1;
+    private boolean mClosingWithAlphaFadeOut;
+    private boolean mHeadsUpAnimatingAway;
+    private boolean mLaunchingAffordance;
+    private boolean mAffordanceHasPreview;
+    private FalsingManager mFalsingManager;
+    private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+
+    private Runnable mHeadsUpExistenceChangedRunnable = () -> {
+        setHeadsUpAnimatingAway(false);
+        notifyBarPanelExpansionChanged();
+    };
+    private NotificationGroupManager mGroupManager;
+    private boolean mShowIconsWhenExpanded;
+    private int mIndicationBottomPadding;
+    private int mAmbientIndicationBottomPadding;
+    private boolean mIsFullWidth;
+    private boolean mBlockingExpansionForCurrentTouch;
+
+    /**
+     * Following variables maintain state of events when input focus transfer may occur.
+     */
+    private boolean mExpectingSynthesizedDown; // expecting to see synthesized DOWN event
+    private boolean mLastEventSynthesizedDown; // last event was synthesized DOWN event
+
+    /**
+     * Current dark amount that follows regular interpolation curve of animation.
+     */
+    private float mInterpolatedDarkAmount;
+
+    /**
+     * Dark amount that animates from 0 to 1 or vice-versa in linear manner, even if the
+     * interpolation curve is different.
+     */
+    private float mLinearDarkAmount;
+
+    private boolean mPulsing;
+    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+    private boolean mUserSetupComplete;
+    private int mQsNotificationTopPadding;
+    private float mExpandOffset;
+    private boolean mHideIconsDuringNotificationLaunch = true;
+    private int mStackScrollerMeasuringPass;
+    private ArrayList<Consumer<ExpandableNotificationRow>>
+            mTrackingHeadsUpListeners =
+            new ArrayList<>();
+    private ArrayList<Runnable> mVerticalTranslationListener = new ArrayList<>();
+    private HeadsUpAppearanceController mHeadsUpAppearanceController;
+
+    private int mPanelAlpha;
+    private Runnable mPanelAlphaEndAction;
+    private float mBottomAreaShadeAlpha;
+    private final ValueAnimator mBottomAreaShadeAlphaAnimator;
+    private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mPanelAlphaEndAction != null) {
+                mPanelAlphaEndAction.run();
+            }
+        }
+    };
+    private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha",
+            NotificationPanelView::setPanelAlphaInternal,
+            NotificationPanelView::getCurrentPanelAlpha,
+            R.id.panel_alpha_animator_tag, R.id.panel_alpha_animator_start_tag,
+            R.id.panel_alpha_animator_end_tag);
+    private final AnimationProperties mPanelAlphaOutPropertiesAnimator =
+            new AnimationProperties().setDuration(150).setCustomInterpolator(
+                    mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT);
+    private final AnimationProperties mPanelAlphaInPropertiesAnimator =
+            new AnimationProperties().setDuration(200).setAnimationFinishListener(
+                    mAnimatorListenerAdapter).setCustomInterpolator(
+                    mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
+    private final NotificationEntryManager mEntryManager;
+
+    private final CommandQueue mCommandQueue;
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+    private final ShadeController mShadeController;
+    private int mDisplayId;
+
+    /**
+     * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged.
+     *
+     * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary
+     * work, check the current id with the cached id.
+     */
+    private int mThemeResId;
+    private KeyguardIndicationController mKeyguardIndicationController;
+    private Consumer<Boolean> mAffordanceLaunchListener;
+    private int mShelfHeight;
+    private Runnable mOnReinflationListener;
+    private int mDarkIconSize;
+    private int mHeadsUpInset;
+    private boolean mHeadsUpPinnedMode;
+    private float mKeyguardHeadsUpShowingAmount = 0.0f;
+    private boolean mShowingKeyguardHeadsUp;
+    private boolean mAllowExpandForSmallExpansion;
+    private Runnable mExpandAfterLayoutRunnable;
+
+    /**
+     * If face auth with bypass is running for the first time after you turn on the screen.
+     * (From aod or screen off)
+     */
+    private boolean mFirstBypassAttempt;
+    /**
+     * If auth happens successfully during {@code mFirstBypassAttempt}, and we should wait until
+     * the keyguard is dismissed to show the status bar.
+     */
+    private boolean mDelayShowingKeyguardStatusBar;
+
+    private PluginManager mPluginManager;
+    private FrameLayout mPluginFrame;
+    private NPVPluginManager mNPVPluginManager;
+
+    @Inject
+    public NotificationPanelViewController(NotificationPanelView view,
+            InjectionInflationController injectionInflationController,
+            NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
+            DynamicPrivacyController dynamicPrivacyController,
+            KeyguardBypassController bypassController, FalsingManager falsingManager,
+            PluginManager pluginManager, ShadeController shadeController,
+            NotificationLockscreenUserManager notificationLockscreenUserManager,
+            NotificationEntryManager notificationEntryManager,
+            KeyguardStateController keyguardStateController,
+            StatusBarStateController statusBarStateController, DozeLog dozeLog,
+            DozeParameters dozeParameters, CommandQueue commandQueue, VibratorHelper vibratorHelper,
+            LatencyTracker latencyTracker, PowerManager powerManager,
+            AccessibilityManager accessibilityManager, @DisplayId int displayId,
+            KeyguardUpdateMonitor keyguardUpdateMonitor, MetricsLogger metricsLogger,
+            ActivityManager activityManager, ZenModeController zenModeController,
+            ConfigurationController configurationController,
+            FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
+        super(view, falsingManager, dozeLog, keyguardStateController,
+                (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
+                latencyTracker, flingAnimationUtilsBuilder);
+        mView = view;
+        mMetricsLogger = metricsLogger;
+        mActivityManager = activityManager;
+        mZenModeController = zenModeController;
+        mConfigurationController = configurationController;
+        mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
+        mView.setWillNotDraw(!DEBUG);
+        mInjectionInflationController = injectionInflationController;
+        mFalsingManager = falsingManager;
+        mPowerManager = powerManager;
+        mWakeUpCoordinator = coordinator;
+        mAccessibilityManager = accessibilityManager;
+        mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+        setPanelAlpha(255, false /* animate */);
+        mCommandQueue = commandQueue;
+        mDisplayId = displayId;
+        mPulseExpansionHandler = pulseExpansionHandler;
+        mDozeParameters = dozeParameters;
+        pulseExpansionHandler.setPulseExpandAbortListener(() -> {
+            if (mQs != null) {
+                mQs.animateHeaderSlidingOut();
+            }
+        });
+        mThemeResId = mView.getContext().getThemeResId();
+        mKeyguardBypassController = bypassController;
+        mUpdateMonitor = keyguardUpdateMonitor;
+        mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
+        KeyguardStateController.Callback
+                keyguardMonitorCallback =
+                new KeyguardStateController.Callback() {
+                    @Override
+                    public void onKeyguardFadingAwayChanged() {
+                        if (!mKeyguardStateController.isKeyguardFadingAway()) {
+                            mFirstBypassAttempt = false;
+                            mDelayShowingKeyguardStatusBar = false;
+                        }
+                    }
+                };
+        mKeyguardStateController.addCallback(keyguardMonitorCallback);
+        DynamicPrivacyControlListener
+                dynamicPrivacyControlListener =
+                new DynamicPrivacyControlListener();
+        dynamicPrivacyController.addListener(dynamicPrivacyControlListener);
+
+        mBottomAreaShadeAlphaAnimator = ValueAnimator.ofFloat(1f, 0);
+        mBottomAreaShadeAlphaAnimator.addUpdateListener(animation -> {
+            mBottomAreaShadeAlpha = (float) animation.getAnimatedValue();
+            updateKeyguardBottomAreaAlpha();
+        });
+        mBottomAreaShadeAlphaAnimator.setDuration(160);
+        mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+        mPluginManager = pluginManager;
+        mShadeController = shadeController;
+        mLockscreenUserManager = notificationLockscreenUserManager;
+        mEntryManager = notificationEntryManager;
+
+        mView.setBackgroundColor(Color.TRANSPARENT);
+        OnAttachStateChangeListener onAttachStateChangeListener = new OnAttachStateChangeListener();
+        mView.addOnAttachStateChangeListener(onAttachStateChangeListener);
+        if (mView.isAttachedToWindow()) {
+            onAttachStateChangeListener.onViewAttachedToWindow(mView);
+        }
+
+        mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener());
+
+        if (DEBUG) {
+            mView.getOverlay().add(new DebugDrawable());
+        }
+
+        onFinishInflate();
+    }
+
+    private void onFinishInflate() {
+        loadDimens();
+        mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
+        mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
+
+        KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
+        mBigClockContainer = mView.findViewById(R.id.big_clock_container);
+        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
+
+        mHomeControlsLayout = mView.findViewById(R.id.home_controls_layout);
+        mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
+        mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
+        mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
+        mNotificationStackScroller.setOverscrollTopChangedListener(mOnOverscrollTopChangedListener);
+        mNotificationStackScroller.setOnEmptySpaceClickListener(mOnEmptySpaceClickListener);
+        addTrackingHeadsUpListener(mNotificationStackScroller::setTrackingHeadsUp);
+        mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
+        mQsNavbarScrim = mView.findViewById(R.id.qs_navbar_scrim);
+        mLastOrientation = mResources.getConfiguration().orientation;
+        mPluginFrame = mView.findViewById(R.id.plugin_frame);
+        if (Settings.System.getInt(mView.getContext().getContentResolver(), "npv_plugin_flag", 0)
+                == 1) {
+            mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        }
+
+
+        initBottomArea();
+
+        mWakeUpCoordinator.setStackScroller(mNotificationStackScroller);
+        mQsFrame = mView.findViewById(R.id.qs_frame);
+        mPulseExpansionHandler.setUp(
+                mNotificationStackScroller, mExpansionCallback, mShadeController);
+        mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() {
+            @Override
+            public void onFullyHiddenChanged(boolean isFullyHidden) {
+                updateKeyguardStatusBarForHeadsUp();
+            }
+
+            @Override
+            public void onPulseExpansionChanged(boolean expandingChanged) {
+                if (mKeyguardBypassController.getBypassEnabled()) {
+                    // Position the notifications while dragging down while pulsing
+                    requestScrollerTopPaddingUpdate(false /* animate */);
+                    updateQSPulseExpansion();
+                }
+            }
+        });
+
+        mPluginManager.addPluginListener(new PluginListener<HomeControlsPlugin>() {
+
+            @Override
+            public void onPluginConnected(HomeControlsPlugin plugin, Context pluginContext) {
+                plugin.sendParentGroup(mHomeControlsLayout);
+            }
+
+            @Override
+            public void onPluginDisconnected(HomeControlsPlugin plugin) {
+
+            }
+        }, HomeControlsPlugin.class, false);
+    }
+
+    @Override
+    protected void loadDimens() {
+        super.loadDimens();
+        mFlingAnimationUtils = mFlingAnimationUtilsBuilder.reset()
+                .setMaxLengthSeconds(0.4f).build();
+        mStatusBarMinHeight = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height);
+        mNotificationsHeaderCollideDistance = mResources.getDimensionPixelSize(
+                R.dimen.header_notifications_collide_distance);
+        mClockPositionAlgorithm.loadDimens(mResources);
+        mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold);
+        mPositionMinSideMargin = mResources.getDimensionPixelSize(
+                R.dimen.notification_panel_min_side_margin);
+        mIndicationBottomPadding = mResources.getDimensionPixelSize(
+                R.dimen.keyguard_indication_bottom_padding);
+        mQsNotificationTopPadding = mResources.getDimensionPixelSize(
+                R.dimen.qs_notification_padding);
+        mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height);
+        mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark);
+        int statusbarHeight = mResources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+        mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize(
+                R.dimen.heads_up_status_bar_padding);
+        mQqsSplitFraction = ((float) mResources.getInteger(R.integer.qqs_split_fraction)) / (
+                mResources.getInteger(R.integer.qqs_split_fraction) + mResources.getInteger(
+                        R.integer.qs_split_fraction));
+    }
+
+    /**
+     * Returns if there's a custom clock being presented.
+     */
+    public boolean hasCustomClock() {
+        return mKeyguardStatusView.hasCustomClock();
+    }
+
+    private void setStatusBar(StatusBar bar) {
+        // TODO: this can be injected.
+        mStatusBar = bar;
+        mKeyguardBottomArea.setStatusBar(mStatusBar);
+    }
+    /**
+     * @see #launchCamera(boolean, int)
+     * @see #setLaunchingAffordance(boolean)
+     */
+    public void setLaunchAffordanceListener(Consumer<Boolean> listener) {
+        mAffordanceLaunchListener = listener;
+    }
+
+    public void updateResources() {
+        int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
+        int panelGravity = mResources.getInteger(R.integer.notification_panel_layout_gravity);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mQsFrame.getLayoutParams();
+        if (lp.width != qsWidth || lp.gravity != panelGravity) {
+            lp.width = qsWidth;
+            lp.gravity = panelGravity;
+            mQsFrame.setLayoutParams(lp);
+        }
+
+        int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
+        lp = (FrameLayout.LayoutParams) mNotificationStackScroller.getLayoutParams();
+        if (lp.width != panelWidth || lp.gravity != panelGravity) {
+            lp.width = panelWidth;
+            lp.gravity = panelGravity;
+            mNotificationStackScroller.setLayoutParams(lp);
+        }
+        int sideMargin = mResources.getDimensionPixelOffset(R.dimen.notification_side_paddings);
+        int topMargin = sideMargin;
+        lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
+        if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
+                || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
+            lp.width = qsWidth;
+            lp.gravity = panelGravity;
+            lp.leftMargin = sideMargin;
+            lp.rightMargin = sideMargin;
+            lp.topMargin = topMargin;
+            mPluginFrame.setLayoutParams(lp);
+        }
+    }
+
+    private void reInflateViews() {
+        updateShowEmptyShadeView();
+
+        // Re-inflate the status view group.
+        int index = mView.indexOfChild(mKeyguardStatusView);
+        mView.removeView(mKeyguardStatusView);
+        mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable(
+                LayoutInflater.from(mView.getContext())).inflate(
+                R.layout.keyguard_status_view, mView, false);
+        mView.addView(mKeyguardStatusView, index);
+
+        // Re-associate the clock container with the keyguard clock switch.
+        mBigClockContainer.removeAllViews();
+        KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
+        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
+
+        // Update keyguard bottom area
+        index = mView.indexOfChild(mKeyguardBottomArea);
+        mView.removeView(mKeyguardBottomArea);
+        KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
+        mKeyguardBottomArea = (KeyguardBottomAreaView) mInjectionInflationController.injectable(
+                LayoutInflater.from(mView.getContext())).inflate(
+                R.layout.keyguard_bottom_area, mView, false);
+        mKeyguardBottomArea.initFrom(oldBottomArea);
+        mView.addView(mKeyguardBottomArea, index);
+        initBottomArea();
+        mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
+        mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
+                mStatusBarStateController.getInterpolatedDozeAmount());
+
+        if (mKeyguardStatusBar != null) {
+            mKeyguardStatusBar.onThemeChanged();
+        }
+
+        setKeyguardStatusViewVisibility(mBarState, false, false);
+        setKeyguardBottomAreaVisibility(mBarState, false);
+        if (mOnReinflationListener != null) {
+            mOnReinflationListener.run();
+        }
+        reinflatePluginContainer();
+    }
+
+    private void reinflatePluginContainer() {
+        int index = mView.indexOfChild(mPluginFrame);
+        mView.removeView(mPluginFrame);
+        mPluginFrame = (FrameLayout) mInjectionInflationController.injectable(
+                LayoutInflater.from(mView.getContext())).inflate(
+                R.layout.status_bar_expanded_plugin_frame, mView, false);
+        mView.addView(mPluginFrame, index);
+
+        Resources res = mView.getResources();
+        int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
+        int panelGravity = mView.getResources().getInteger(
+                R.integer.notification_panel_layout_gravity);
+        FrameLayout.LayoutParams lp;
+        int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
+        int topMargin = res.getDimensionPixelOffset(
+                com.android.internal.R.dimen.quick_qs_total_height);
+        if (Utils.useQsMediaPlayer(mView.getContext())) {
+            topMargin = res.getDimensionPixelOffset(
+                    com.android.internal.R.dimen.quick_qs_total_height_with_media);
+        }
+        lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
+        if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
+                || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
+            lp.width = qsWidth;
+            lp.gravity = panelGravity;
+            lp.leftMargin = sideMargin;
+            lp.rightMargin = sideMargin;
+            lp.topMargin = topMargin;
+            mPluginFrame.setLayoutParams(lp);
+        }
+
+        if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
+    }
+
+    private void initBottomArea() {
+        mAffordanceHelper = new KeyguardAffordanceHelper(
+                mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
+        mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
+        mKeyguardBottomArea.setStatusBar(mStatusBar);
+        mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
+    }
+
+    public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
+        mKeyguardIndicationController = indicationController;
+        mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
+    }
+
+    private void updateGestureExclusionRect() {
+        Rect exclusionRect = calculateGestureExclusionRect();
+        mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST
+                : Collections.singletonList(exclusionRect));
+    }
+
+    private Rect calculateGestureExclusionRect() {
+        Rect exclusionRect = null;
+        Region touchableRegion = mHeadsUpManager.calculateTouchableRegion();
+        if (isFullyCollapsed() && touchableRegion != null) {
+            // Note: The heads up manager also calculates the non-pinned touchable region
+            exclusionRect = touchableRegion.getBounds();
+        }
+        return exclusionRect != null ? exclusionRect : EMPTY_RECT;
+    }
+
+    private void setIsFullWidth(boolean isFullWidth) {
+        mIsFullWidth = isFullWidth;
+        mNotificationStackScroller.setIsFullWidth(isFullWidth);
+    }
+
+    private void startQsSizeChangeAnimation(int oldHeight, final int newHeight) {
+        if (mQsSizeChangeAnimator != null) {
+            oldHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
+            mQsSizeChangeAnimator.cancel();
+        }
+        mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight);
+        mQsSizeChangeAnimator.setDuration(300);
+        mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                requestScrollerTopPaddingUpdate(false /* animate */);
+                requestPanelHeightUpdate();
+                int height = (int) mQsSizeChangeAnimator.getAnimatedValue();
+                mQs.setHeightOverride(height);
+            }
+        });
+        mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mQsSizeChangeAnimator = null;
+            }
+        });
+        mQsSizeChangeAnimator.start();
+    }
+
+    /**
+     * Positions the clock and notifications dynamically depending on how many notifications are
+     * showing.
+     */
+    private void positionClockAndNotifications() {
+        boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+        boolean animateClock = animate || mAnimateNextPositionUpdate;
+        int stackScrollerPadding;
+        if (mBarState != StatusBarState.KEYGUARD) {
+            stackScrollerPadding = getUnlockedStackScrollerPadding();
+        } else {
+            int totalHeight = mView.getHeight();
+            int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding);
+            int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight);
+            boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
+            final boolean
+                    hasVisibleNotifications =
+                    !bypassEnabled && mNotificationStackScroller.getVisibleNotificationCount() != 0;
+            mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications);
+            mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding,
+                    mNotificationStackScroller.getIntrinsicContentHeight(), getExpandedFraction(),
+                    totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f
+                            - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(),
+                    hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
+                    bypassEnabled, getUnlockedStackScrollerPadding());
+            mClockPositionAlgorithm.run(mClockPositionResult);
+            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X,
+                    mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock);
+            PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y,
+                    mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock);
+            updateNotificationTranslucency();
+            updateClock();
+            stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded;
+        }
+        mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
+        mKeyguardBottomArea.setAntiBurnInOffsetX(mClockPositionResult.clockX);
+
+        mStackScrollerMeasuringPass++;
+        requestScrollerTopPaddingUpdate(animate);
+        mStackScrollerMeasuringPass = 0;
+        mAnimateNextPositionUpdate = false;
+    }
+
+    /**
+     * @return the padding of the stackscroller when unlocked
+     */
+    private int getUnlockedStackScrollerPadding() {
+        return (mQs != null ? mQs.getHeader().getHeight() : 0) + mQsPeekHeight
+                + mQsNotificationTopPadding;
+    }
+
+    /**
+     * @param maximum the maximum to return at most
+     * @return the maximum keyguard notifications that can fit on the screen
+     */
+    public int computeMaxKeyguardNotifications(int maximum) {
+        float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
+        int notificationPadding = Math.max(
+                1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
+        NotificationShelf shelf = mNotificationStackScroller.getNotificationShelf();
+        float
+                shelfSize =
+                shelf.getVisibility() == View.GONE ? 0
+                        : shelf.getIntrinsicHeight() + notificationPadding;
+        float
+                availableSpace =
+                mNotificationStackScroller.getHeight() - minPadding - shelfSize - Math.max(
+                        mIndicationBottomPadding, mAmbientIndicationBottomPadding)
+                        - mKeyguardStatusView.getLogoutButtonHeight();
+        int count = 0;
+        for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                continue;
+            }
+            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+            boolean
+                    suppressedSummary =
+                    mGroupManager != null && mGroupManager.isSummaryOfSuppressedGroup(
+                            row.getEntry().getSbn());
+            if (suppressedSummary) {
+                continue;
+            }
+            if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) {
+                continue;
+            }
+            if (row.isRemoved()) {
+                continue;
+            }
+            availableSpace -= child.getMinHeight(true /* ignoreTemporaryStates */)
+                    + notificationPadding;
+            if (availableSpace >= 0 && count < maximum) {
+                count++;
+            } else if (availableSpace > -shelfSize) {
+                // if we are exactly the last view, then we can show us still!
+                for (int j = i + 1; j < mNotificationStackScroller.getChildCount(); j++) {
+                    if (mNotificationStackScroller.getChildAt(
+                            j) instanceof ExpandableNotificationRow) {
+                        return count;
+                    }
+                }
+                count++;
+                return count;
+            } else {
+                return count;
+            }
+        }
+        return count;
+    }
+
+    private void updateClock() {
+        if (!mKeyguardStatusViewAnimating) {
+            mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha);
+        }
+    }
+
+    public void animateToFullShade(long delay) {
+        mNotificationStackScroller.goToFullShade(delay);
+        mView.requestLayout();
+        mAnimateNextPositionUpdate = true;
+    }
+
+    public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
+        mQsExpansionEnabled = qsExpansionEnabled;
+        if (mQs == null) return;
+        mQs.setHeaderClickable(qsExpansionEnabled);
+    }
+
+    @Override
+    public void resetViews(boolean animate) {
+        mIsLaunchTransitionFinished = false;
+        mBlockTouches = false;
+        if (!mLaunchingAffordance) {
+            mAffordanceHelper.reset(false);
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+        }
+        mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
+                true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
+        if (animate) {
+            animateCloseQs(true /* animateAway */);
+        } else {
+            closeQs();
+        }
+        mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, animate,
+                !animate /* cancelAnimators */);
+        mNotificationStackScroller.resetScrollPosition();
+    }
+
+    @Override
+    public void collapse(boolean delayed, float speedUpFactor) {
+        if (!canPanelBeCollapsed()) {
+            return;
+        }
+
+        if (mQsExpanded) {
+            mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
+        }
+        super.collapse(delayed, speedUpFactor);
+    }
+
+    public void closeQs() {
+        cancelQsAnimation();
+        setQsExpansion(mQsMinExpansionHeight);
+    }
+
+    public void cancelAnimation() {
+        mView.animate().cancel();
+    }
+
+
+    /**
+     * Animate QS closing by flinging it.
+     * If QS is expanded, it will collapse into QQS and stop.
+     *
+     * @param animateAway Do not stop when QS becomes QQS. Fling until QS isn't visible anymore.
+     */
+    public void animateCloseQs(boolean animateAway) {
+        if (mQsExpansionAnimator != null) {
+            if (!mQsAnimatorExpand) {
+                return;
+            }
+            float height = mQsExpansionHeight;
+            mQsExpansionAnimator.cancel();
+            setQsExpansion(height);
+        }
+        flingSettings(0 /* vel */, animateAway ? FLING_HIDE : FLING_COLLAPSE);
+    }
+
+    public void expandWithQs() {
+        if (mQsExpansionEnabled) {
+            mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
+        }
+        if (isFullyCollapsed()) {
+            expand(true /* animate */);
+        } else {
+            flingSettings(0 /* velocity */, FLING_EXPAND);
+        }
+    }
+
+    public void expandWithoutQs() {
+        if (isQsExpanded()) {
+            flingSettings(0 /* velocity */, FLING_COLLAPSE);
+        } else {
+            expand(true /* animate */);
+        }
+    }
+
+    @Override
+    public void fling(float vel, boolean expand) {
+        GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
+        if (gr != null) {
+            gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
+        }
+        super.fling(vel, expand);
+    }
+
+    @Override
+    protected void flingToHeight(float vel, boolean expand, float target,
+            float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
+        mHeadsUpTouchHelper.notifyFling(!expand);
+        setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
+        super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
+    }
+
+
+    private boolean onQsIntercept(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float x = event.getX(pointerIndex);
+        final float y = event.getY(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                initVelocityTracker();
+                trackMovement(event);
+                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                    mView.getParent().requestDisallowInterceptTouchEvent(true);
+                }
+                if (mQsExpansionAnimator != null) {
+                    onQsExpansionStarted();
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mQsTracking = true;
+                    mNotificationStackScroller.cancelLongPress();
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchX = event.getX(newIndex);
+                    mInitialTouchY = event.getY(newIndex);
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                trackMovement(event);
+                if (mQsTracking) {
+
+                    // Already tracking because onOverscrolled was called. We need to update here
+                    // so we don't stop for a frame until the next touch event gets handled in
+                    // onTouchEvent.
+                    setQsExpansion(h + mInitialHeightOnTouch);
+                    trackMovement(event);
+                    return true;
+                }
+                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
+                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                    mQsTracking = true;
+                    onQsExpansionStarted();
+                    notifyExpandingFinished();
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mNotificationStackScroller.cancelLongPress();
+                    return true;
+                }
+                break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                trackMovement(event);
+                if (mQsTracking) {
+                    flingQsWithCurrentVelocity(y,
+                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+                    mQsTracking = false;
+                }
+                break;
+        }
+        return false;
+    }
+
+    @Override
+    protected boolean isInContentBounds(float x, float y) {
+        float stackScrollerX = mNotificationStackScroller.getX();
+        return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
+                && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
+    }
+
+    private void initDownStates(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            mOnlyAffordanceInThisMotion = false;
+            mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
+            mDozingOnDown = isDozing();
+            mDownX = event.getX();
+            mDownY = event.getY();
+            mCollapsedOnDown = isFullyCollapsed();
+            mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp();
+            mAllowExpandForSmallExpansion = mExpectingSynthesizedDown;
+            mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown;
+            if (mExpectingSynthesizedDown) {
+                mLastEventSynthesizedDown = true;
+            } else {
+                // down but not synthesized motion event.
+                mLastEventSynthesizedDown = false;
+            }
+        } else {
+            // not down event at all.
+            mLastEventSynthesizedDown = false;
+        }
+    }
+
+    private void flingQsWithCurrentVelocity(float y, boolean isCancelMotionEvent) {
+        float vel = getCurrentQSVelocity();
+        final boolean expandsQs = flingExpandsQs(vel);
+        if (expandsQs) {
+            logQsSwipeDown(y);
+        }
+        flingSettings(vel, expandsQs && !isCancelMotionEvent ? FLING_EXPAND : FLING_COLLAPSE);
+    }
+
+    private void logQsSwipeDown(float y) {
+        float vel = getCurrentQSVelocity();
+        final int
+                gesture =
+                mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS
+                        : MetricsEvent.ACTION_SHADE_QS_PULL;
+        mLockscreenGestureLogger.write(gesture,
+                (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
+                (int) (vel / mStatusBar.getDisplayDensity()));
+    }
+
+    private boolean flingExpandsQs(float vel) {
+        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
+            return false;
+        }
+        if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+            return getQsExpansionFraction() > 0.5f;
+        } else {
+            return vel > 0;
+        }
+    }
+
+    private boolean isFalseTouch() {
+        if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
+            return false;
+        }
+        if (mFalsingManager.isClassifierEnabled()) {
+            return mFalsingManager.isFalseTouch();
+        }
+        return !mQsTouchAboveFalsingThreshold;
+    }
+
+    private float getQsExpansionFraction() {
+        return Math.min(
+                1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
+                        - mQsMinExpansionHeight));
+    }
+
+    @Override
+    protected boolean shouldExpandWhenNotFlinging() {
+        if (super.shouldExpandWhenNotFlinging()) {
+            return true;
+        }
+        if (mAllowExpandForSmallExpansion) {
+            // When we get a touch that came over from launcher, the velocity isn't always correct
+            // Let's err on expanding if the gesture has been reasonably slow
+            long timeSinceDown = SystemClock.uptimeMillis() - mDownTime;
+            return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER;
+        }
+        return false;
+    }
+
+    @Override
+    protected float getOpeningHeight() {
+        return mNotificationStackScroller.getOpeningHeight();
+    }
+
+
+    private boolean handleQsTouch(MotionEvent event) {
+        final int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
+                && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
+
+            // Down in the empty area while fully expanded - go to QS.
+            mQsTracking = true;
+            mConflictingQsExpansionGesture = true;
+            onQsExpansionStarted();
+            mInitialHeightOnTouch = mQsExpansionHeight;
+            mInitialTouchY = event.getX();
+            mInitialTouchX = event.getY();
+        }
+        if (!isFullyCollapsed()) {
+            handleQsDown(event);
+        }
+        if (!mQsExpandImmediate && mQsTracking) {
+            onQsTouch(event);
+            if (!mConflictingQsExpansionGesture) {
+                return true;
+            }
+        }
+        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+            mConflictingQsExpansionGesture = false;
+        }
+        if (action == MotionEvent.ACTION_DOWN && isFullyCollapsed() && mQsExpansionEnabled) {
+            mTwoFingerQsExpandPossible = true;
+        }
+        if (mTwoFingerQsExpandPossible && isOpenQsEvent(event) && event.getY(event.getActionIndex())
+                < mStatusBarMinHeight) {
+            mMetricsLogger.count(COUNTER_PANEL_OPEN_QS, 1);
+            mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
+            requestPanelHeightUpdate();
+
+            // Normally, we start listening when the panel is expanded, but here we need to start
+            // earlier so the state is already up to date when dragging down.
+            setListening(true);
+        }
+        if (isQsSplitEnabled() && !mKeyguardShowing) {
+            if (mQsExpandImmediate) {
+                mNotificationStackScroller.setVisibility(View.GONE);
+                mQsFrame.setVisibility(View.VISIBLE);
+                mHomeControlsLayout.setVisibility(View.VISIBLE);
+            } else {
+                mNotificationStackScroller.setVisibility(View.VISIBLE);
+                mQsFrame.setVisibility(View.GONE);
+                mHomeControlsLayout.setVisibility(View.GONE);
+            }
+        }
+        return false;
+    }
+
+    private boolean isInQsArea(float x, float y) {
+        return (x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()) && (
+                y <= mNotificationStackScroller.getBottomMostNotificationBottom()
+                        || y <= mQs.getView().getY() + mQs.getView().getHeight());
+    }
+
+    private boolean isOnQsEndArea(float x) {
+        if (!isQsSplitEnabled()) return false;
+        if (mView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
+            return x >= mQsFrame.getX() + mQqsSplitFraction * mQsFrame.getWidth()
+                    && x <= mQsFrame.getX() + mQsFrame.getWidth();
+        } else {
+            return x >= mQsFrame.getX()
+                    && x <= mQsFrame.getX() + (1 - mQqsSplitFraction) * mQsFrame.getWidth();
+        }
+    }
+
+    private boolean isOpenQsEvent(MotionEvent event) {
+        final int pointerCount = event.getPointerCount();
+        final int action = event.getActionMasked();
+
+        final boolean
+                twoFingerDrag =
+                action == MotionEvent.ACTION_POINTER_DOWN && pointerCount == 2;
+
+        final boolean
+                stylusButtonClickDrag =
+                action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
+                        MotionEvent.BUTTON_STYLUS_PRIMARY) || event.isButtonPressed(
+                        MotionEvent.BUTTON_STYLUS_SECONDARY));
+
+        final boolean
+                mouseButtonClickDrag =
+                action == MotionEvent.ACTION_DOWN && (event.isButtonPressed(
+                        MotionEvent.BUTTON_SECONDARY) || event.isButtonPressed(
+                        MotionEvent.BUTTON_TERTIARY));
+
+        final boolean onHeaderRight = isOnQsEndArea(event.getX());
+
+        return twoFingerDrag || stylusButtonClickDrag || mouseButtonClickDrag || onHeaderRight;
+    }
+
+    private void handleQsDown(MotionEvent event) {
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept(
+                event.getX(), event.getY(), -1)) {
+            mFalsingManager.onQsDown();
+            mQsTracking = true;
+            onQsExpansionStarted();
+            mInitialHeightOnTouch = mQsExpansionHeight;
+            mInitialTouchY = event.getX();
+            mInitialTouchX = event.getY();
+
+            // If we interrupt an expansion gesture here, make sure to update the state correctly.
+            notifyExpandingFinished();
+        }
+    }
+
+    /**
+     * Input focus transfer is about to happen.
+     */
+    public void startWaitingForOpenPanelGesture() {
+        if (!isFullyCollapsed()) {
+            return;
+        }
+        mExpectingSynthesizedDown = true;
+        onTrackingStarted();
+        updatePanelExpanded();
+    }
+
+    /**
+     * Called when this view is no longer waiting for input focus transfer.
+     *
+     * There are two scenarios behind this function call. First, input focus transfer
+     * has successfully happened and this view already received synthetic DOWN event.
+     * (mExpectingSynthesizedDown == false). Do nothing.
+     *
+     * Second, before input focus transfer finished, user may have lifted finger
+     * in previous window and this window never received synthetic DOWN event.
+     * (mExpectingSynthesizedDown == true).
+     * In this case, we use the velocity to trigger fling event.
+     *
+     * @param velocity unit is in px / millis
+     */
+    public void stopWaitingForOpenPanelGesture(final float velocity) {
+        if (mExpectingSynthesizedDown) {
+            mExpectingSynthesizedDown = false;
+            maybeVibrateOnOpening();
+            Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0,
+                    true /* expand */);
+            if (mStatusBar.getStatusBarWindow().getHeight() != mStatusBar.getStatusBarHeight()) {
+                // The panel is already expanded to its full size, let's expand directly
+                runnable.run();
+            } else {
+                mExpandAfterLayoutRunnable = runnable;
+            }
+            onTrackingStopped(false);
+        }
+    }
+
+    @Override
+    protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
+        boolean expands = super.flingExpands(vel, vectorVel, x, y);
+
+        // If we are already running a QS expansion, make sure that we keep the panel open.
+        if (mQsExpansionAnimator != null) {
+            expands = true;
+        }
+        return expands;
+    }
+
+    @Override
+    protected boolean shouldGestureWaitForTouchSlop() {
+        if (mExpectingSynthesizedDown) {
+            mExpectingSynthesizedDown = false;
+            return false;
+        }
+        return isFullyCollapsed() || mBarState != StatusBarState.SHADE;
+    }
+
+    @Override
+    protected boolean shouldGestureIgnoreXTouchSlop(float x, float y) {
+        return !mAffordanceHelper.isOnAffordanceIcon(x, y);
+    }
+
+    private void onQsTouch(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+        final float h = y - mInitialTouchY;
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mQsTracking = true;
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                onQsExpansionStarted();
+                mInitialHeightOnTouch = mQsExpansionHeight;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                setQsExpansion(h + mInitialHeightOnTouch);
+                if (h >= getFalsingThreshold()) {
+                    mQsTouchAboveFalsingThreshold = true;
+                }
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mQsTracking = false;
+                mTrackingPointer = -1;
+                trackMovement(event);
+                float fraction = getQsExpansionFraction();
+                if (fraction != 0f || y >= mInitialTouchY) {
+                    flingQsWithCurrentVelocity(y,
+                            event.getActionMasked() == MotionEvent.ACTION_CANCEL);
+                }
+                if (mQsVelocityTracker != null) {
+                    mQsVelocityTracker.recycle();
+                    mQsVelocityTracker = null;
+                }
+                break;
+        }
+    }
+
+    private int getFalsingThreshold() {
+        float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+        return (int) (mQsFalsingThreshold * factor);
+    }
+
+    private void setOverScrolling(boolean overscrolling) {
+        mStackScrollerOverscrolling = overscrolling;
+        if (mQs == null) return;
+        mQs.setOverscrolling(overscrolling);
+    }
+
+    private void onQsExpansionStarted() {
+        onQsExpansionStarted(0);
+    }
+
+    protected void onQsExpansionStarted(int overscrollAmount) {
+        cancelQsAnimation();
+        cancelHeightAnimator();
+
+        // Reset scroll position and apply that position to the expanded height.
+        float height = mQsExpansionHeight - overscrollAmount;
+        setQsExpansion(height);
+        requestPanelHeightUpdate();
+        mNotificationStackScroller.checkSnoozeLeavebehind();
+
+        // When expanding QS, let's authenticate the user if possible,
+        // this will speed up notification actions.
+        if (height == 0) {
+            mStatusBar.requestFaceAuth();
+        }
+    }
+
+    private void setQsExpanded(boolean expanded) {
+        boolean changed = mQsExpanded != expanded;
+        if (changed) {
+            mQsExpanded = expanded;
+            updateQsState();
+            requestPanelHeightUpdate();
+            mFalsingManager.setQsExpanded(expanded);
+            mStatusBar.setQsExpanded(expanded);
+            mNotificationContainerParent.setQsExpanded(expanded);
+            mPulseExpansionHandler.setQsExpanded(expanded);
+            mKeyguardBypassController.setQSExpanded(expanded);
+        }
+    }
+
+    private void maybeAnimateBottomAreaAlpha() {
+        mBottomAreaShadeAlphaAnimator.cancel();
+        if (mBarState == StatusBarState.SHADE_LOCKED) {
+            mBottomAreaShadeAlphaAnimator.start();
+        } else {
+            mBottomAreaShadeAlpha = 1f;
+        }
+    }
+
+    private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardStatusViewAnimating = false;
+            mKeyguardStatusView.setVisibility(View.INVISIBLE);
+        }
+    };
+
+    private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardStatusViewAnimating = false;
+            mKeyguardStatusView.setVisibility(View.GONE);
+        }
+    };
+
+    private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardStatusViewAnimating = false;
+        }
+    };
+
+    private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardStatusBar.setVisibility(View.INVISIBLE);
+            mKeyguardStatusBar.setAlpha(1f);
+            mKeyguardStatusBarAnimateAlpha = 1f;
+        }
+    };
+
+    private void animateKeyguardStatusBarOut() {
+        ValueAnimator anim = ValueAnimator.ofFloat(mKeyguardStatusBar.getAlpha(), 0f);
+        anim.addUpdateListener(mStatusBarAnimateAlphaListener);
+        anim.setStartDelay(mKeyguardStateController.isKeyguardFadingAway()
+                ? mKeyguardStateController.getKeyguardFadingAwayDelay() : 0);
+
+        long duration;
+        if (mKeyguardStateController.isKeyguardFadingAway()) {
+            duration = mKeyguardStateController.getShortenedFadingAwayDuration();
+        } else {
+            duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
+        }
+        anim.setDuration(duration);
+
+        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimateKeyguardStatusBarInvisibleEndRunnable.run();
+            }
+        });
+        anim.start();
+    }
+
+    private final ValueAnimator.AnimatorUpdateListener
+            mStatusBarAnimateAlphaListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    mKeyguardStatusBarAnimateAlpha = (float) animation.getAnimatedValue();
+                    updateHeaderKeyguardAlpha();
+                }
+            };
+
+    private void animateKeyguardStatusBarIn(long duration) {
+        mKeyguardStatusBar.setVisibility(View.VISIBLE);
+        mKeyguardStatusBar.setAlpha(0f);
+        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
+        anim.addUpdateListener(mStatusBarAnimateAlphaListener);
+        anim.setDuration(duration);
+        anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        anim.start();
+    }
+
+    private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mKeyguardBottomArea.setVisibility(View.GONE);
+        }
+    };
+
+    private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) {
+        mKeyguardBottomArea.animate().cancel();
+        if (goingToFullShade) {
+            mKeyguardBottomArea.animate().alpha(0f).setStartDelay(
+                    mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
+                    mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
+                    Interpolators.ALPHA_OUT).withEndAction(
+                    mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
+        } else if (statusBarState == StatusBarState.KEYGUARD
+                || statusBarState == StatusBarState.SHADE_LOCKED) {
+            mKeyguardBottomArea.setVisibility(View.VISIBLE);
+            mKeyguardBottomArea.setAlpha(1f);
+        } else {
+            mKeyguardBottomArea.setVisibility(View.GONE);
+        }
+    }
+
+    private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway,
+            boolean goingToFullShade) {
+        mKeyguardStatusView.animate().cancel();
+        mKeyguardStatusViewAnimating = false;
+        if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
+                && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
+            mKeyguardStatusViewAnimating = true;
+            mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
+                    160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
+                    mAnimateKeyguardStatusViewGoneEndRunnable);
+            if (keyguardFadingAway) {
+                mKeyguardStatusView.animate().setStartDelay(
+                        mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration(
+                        mKeyguardStateController.getShortenedFadingAwayDuration()).start();
+            }
+        } else if (mBarState == StatusBarState.SHADE_LOCKED
+                && statusBarState == StatusBarState.KEYGUARD) {
+            mKeyguardStatusView.setVisibility(View.VISIBLE);
+            mKeyguardStatusViewAnimating = true;
+            mKeyguardStatusView.setAlpha(0f);
+            mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
+                    320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
+                    mAnimateKeyguardStatusViewVisibleEndRunnable);
+        } else if (statusBarState == StatusBarState.KEYGUARD) {
+            if (keyguardFadingAway) {
+                mKeyguardStatusViewAnimating = true;
+                mKeyguardStatusView.animate().alpha(0).translationYBy(
+                        -getHeight() * 0.05f).setInterpolator(
+                        Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay(
+                        0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start();
+            } else {
+                mKeyguardStatusView.setVisibility(View.VISIBLE);
+                mKeyguardStatusView.setAlpha(1f);
+            }
+        } else {
+            mKeyguardStatusView.setVisibility(View.GONE);
+            mKeyguardStatusView.setAlpha(1f);
+        }
+    }
+
+    private void updateQsState() {
+        mNotificationStackScroller.setQsExpanded(mQsExpanded);
+        mNotificationStackScroller.setScrollingEnabled(
+                mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
+                        || mQsExpansionFromOverscroll));
+        updateEmptyShadeView();
+        if (mNPVPluginManager != null) {
+            mNPVPluginManager.changeVisibility(
+                    (mBarState != StatusBarState.KEYGUARD) ? View.VISIBLE : View.INVISIBLE);
+        }
+        mQsNavbarScrim.setVisibility(
+                mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
+                        && mQsScrimEnabled ? View.VISIBLE : View.INVISIBLE);
+        if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
+            mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
+        }
+        if (mQs == null) return;
+        mQs.setExpanded(mQsExpanded);
+    }
+
+    private void setQsExpansion(float height) {
+        height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
+        mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
+        if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
+                && !mDozing) {
+            setQsExpanded(true);
+        } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
+            setQsExpanded(false);
+        }
+        mQsExpansionHeight = height;
+        updateQsExpansion();
+        requestScrollerTopPaddingUpdate(false /* animate */);
+        updateHeaderKeyguardAlpha();
+        if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) {
+            updateKeyguardBottomAreaAlpha();
+            updateBigClockAlpha();
+        }
+        if (mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling
+                && mQsScrimEnabled) {
+            mQsNavbarScrim.setAlpha(getQsExpansionFraction());
+        }
+
+        if (mAccessibilityManager.isEnabled()) {
+            mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+        }
+
+        if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
+                && mFalsingManager.shouldEnforceBouncer()) {
+            mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
+                    false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
+        }
+        for (int i = 0; i < mExpansionListeners.size(); i++) {
+            mExpansionListeners.get(i).onQsExpansionChanged(
+                    mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0);
+        }
+        if (DEBUG) {
+            mView.invalidate();
+        }
+    }
+
+    protected void updateQsExpansion() {
+        if (mQs == null) return;
+        float qsExpansionFraction = getQsExpansionFraction();
+        mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
+        int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
+        if (mNPVPluginManager != null) {
+            mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+        }
+        mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
+    }
+
+    private String determineAccessibilityPaneTitle() {
+        if (mQs != null && mQs.isCustomizing()) {
+            return mResources.getString(R.string.accessibility_desc_quick_settings_edit);
+        } else if (mQsExpansionHeight != 0.0f && mQsFullyExpanded) {
+            // Upon initialisation when we are not layouted yet we don't want to announce that we
+            // are fully expanded, hence the != 0.0f check.
+            return mResources.getString(R.string.accessibility_desc_quick_settings);
+        } else if (mBarState == StatusBarState.KEYGUARD) {
+            return mResources.getString(R.string.accessibility_desc_lock_screen);
+        } else {
+            return mResources.getString(R.string.accessibility_desc_notification_shade);
+        }
+    }
+
+    private float calculateQsTopPadding() {
+        if (mKeyguardShowing && (mQsExpandImmediate
+                || mIsExpanding && mQsExpandedWhenExpandingStarted)) {
+
+            // Either QS pushes the notifications down when fully expanded, or QS is fully above the
+            // notifications (mostly on tablets). maxNotificationPadding denotes the normal top
+            // padding on Keyguard, maxQsPadding denotes the top padding from the quick settings
+            // panel. We need to take the maximum and linearly interpolate with the panel expansion
+            // for a nice motion.
+            int maxNotificationPadding = getKeyguardNotificationStaticPadding();
+            int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
+            int max = mBarState == StatusBarState.KEYGUARD ? Math.max(
+                    maxNotificationPadding, maxQsPadding) : maxQsPadding;
+            return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
+                    getExpandedFraction());
+        } else if (mQsSizeChangeAnimator != null) {
+            return Math.max(
+                    (int) mQsSizeChangeAnimator.getAnimatedValue(),
+                    getKeyguardNotificationStaticPadding());
+        } else if (mKeyguardShowing) {
+            // We can only do the smoother transition on Keyguard when we also are not collapsing
+            // from a scrolled quick settings.
+            return MathUtils.lerp((float) getKeyguardNotificationStaticPadding(),
+                    (float) (mQsMaxExpansionHeight + mQsNotificationTopPadding),
+                    getQsExpansionFraction());
+        } else {
+            return mQsExpansionHeight + mQsNotificationTopPadding;
+        }
+    }
+
+    /**
+     * @return the topPadding of notifications when on keyguard not respecting quick settings
+     * expansion
+     */
+    private int getKeyguardNotificationStaticPadding() {
+        if (!mKeyguardShowing) {
+            return 0;
+        }
+        if (!mKeyguardBypassController.getBypassEnabled()) {
+            return mClockPositionResult.stackScrollerPadding;
+        }
+        int collapsedPosition = mHeadsUpInset;
+        if (!mNotificationStackScroller.isPulseExpanding()) {
+            return collapsedPosition;
+        } else {
+            int expandedPosition = mClockPositionResult.stackScrollerPadding;
+            return (int) MathUtils.lerp(collapsedPosition, expandedPosition,
+                    mNotificationStackScroller.calculateAppearFractionBypass());
+        }
+    }
+
+
+    protected void requestScrollerTopPaddingUpdate(boolean animate) {
+        mNotificationStackScroller.updateTopPadding(calculateQsTopPadding(), animate);
+        if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) {
+            // update the position of the header
+            updateQsExpansion();
+        }
+    }
+
+
+    private void updateQSPulseExpansion() {
+        if (mQs != null) {
+            mQs.setShowCollapsedOnKeyguard(
+                    mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()
+                            && mNotificationStackScroller.isPulseExpanding());
+        }
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mQsVelocityTracker != null) mQsVelocityTracker.addMovement(event);
+    }
+
+    private void initVelocityTracker() {
+        if (mQsVelocityTracker != null) {
+            mQsVelocityTracker.recycle();
+        }
+        mQsVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentQSVelocity() {
+        if (mQsVelocityTracker == null) {
+            return 0;
+        }
+        mQsVelocityTracker.computeCurrentVelocity(1000);
+        return mQsVelocityTracker.getYVelocity();
+    }
+
+    private void cancelQsAnimation() {
+        if (mQsExpansionAnimator != null) {
+            mQsExpansionAnimator.cancel();
+        }
+    }
+
+    /**
+     * @see #flingSettings(float, int, Runnable, boolean)
+     */
+    public void flingSettings(float vel, int type) {
+        flingSettings(vel, type, null, false /* isClick */);
+    }
+
+    /**
+     * Animates QS or QQS as if the user had swiped up or down.
+     *
+     * @param vel              Finger velocity or 0 when not initiated by touch events.
+     * @param type             Either {@link #FLING_EXPAND}, {@link #FLING_COLLAPSE} or {@link
+     *                         #FLING_HIDE}.
+     * @param onFinishRunnable Runnable to be executed at the end of animation.
+     * @param isClick          If originated by click (different interpolator and duration.)
+     */
+    protected void flingSettings(float vel, int type, final Runnable onFinishRunnable,
+            boolean isClick) {
+        float target;
+        switch (type) {
+            case FLING_EXPAND:
+                target = mQsMaxExpansionHeight;
+                break;
+            case FLING_COLLAPSE:
+                target = mQsMinExpansionHeight;
+                break;
+            case FLING_HIDE:
+            default:
+                target = 0;
+        }
+        if (target == mQsExpansionHeight) {
+            if (onFinishRunnable != null) {
+                onFinishRunnable.run();
+            }
+            return;
+        }
+
+        // If we move in the opposite direction, reset velocity and use a different duration.
+        boolean oppositeDirection = false;
+        boolean expanding = type == FLING_EXPAND;
+        if (vel > 0 && !expanding || vel < 0 && expanding) {
+            vel = 0;
+            oppositeDirection = true;
+        }
+        ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
+        if (isClick) {
+            animator.setInterpolator(Interpolators.TOUCH_RESPONSE);
+            animator.setDuration(368);
+        } else {
+            mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
+        }
+        if (oppositeDirection) {
+            animator.setDuration(350);
+        }
+        animator.addUpdateListener(animation -> {
+            setQsExpansion((Float) animation.getAnimatedValue());
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mNotificationStackScroller.resetCheckSnoozeLeavebehind();
+                mQsExpansionAnimator = null;
+                if (onFinishRunnable != null) {
+                    onFinishRunnable.run();
+                }
+            }
+        });
+        animator.start();
+        mQsExpansionAnimator = animator;
+        mQsAnimatorExpand = expanding;
+    }
+
+    /**
+     * @return Whether we should intercept a gesture to open Quick Settings.
+     */
+    private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
+        if (!mQsExpansionEnabled || mCollapsedOnDown || (mKeyguardShowing
+                && mKeyguardBypassController.getBypassEnabled())) {
+            return false;
+        }
+        View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader();
+        final boolean
+                onHeader =
+                x >= mQsFrame.getX() && x <= mQsFrame.getX() + mQsFrame.getWidth()
+                        && y >= header.getTop() && y <= header.getBottom();
+        if (mQsExpanded) {
+            return onHeader || (yDiff < 0 && isInQsArea(x, y));
+        } else {
+            return onHeader;
+        }
+    }
+
+    @Override
+    protected boolean isScrolledToBottom() {
+        if (!isInSettings()) {
+            return mBarState == StatusBarState.KEYGUARD
+                    || mNotificationStackScroller.isScrolledToBottom();
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    protected int getMaxPanelHeight() {
+        if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
+            return getMaxPanelHeightBypass();
+        } else {
+            return getMaxPanelHeightNonBypass();
+        }
+    }
+
+    private int getMaxPanelHeightNonBypass() {
+        int min = mStatusBarMinHeight;
+        if (!(mBarState == StatusBarState.KEYGUARD)
+                && mNotificationStackScroller.getNotGoneChildCount() == 0) {
+            int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
+            min = Math.max(min, minHeight);
+        }
+        int maxHeight;
+        if (mQsExpandImmediate || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted
+                || mPulsing) {
+            maxHeight = calculatePanelHeightQsExpanded();
+        } else {
+            maxHeight = calculatePanelHeightShade();
+        }
+        maxHeight = Math.max(maxHeight, min);
+        return maxHeight;
+    }
+
+    private int getMaxPanelHeightBypass() {
+        int position =
+                mClockPositionAlgorithm.getExpandedClockPosition()
+                        + mKeyguardStatusView.getHeight();
+        if (mNotificationStackScroller.getVisibleNotificationCount() != 0) {
+            position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f;
+        }
+        return position;
+    }
+
+    public boolean isInSettings() {
+        return mQsExpanded;
+    }
+
+    public boolean isExpanding() {
+        return mIsExpanding;
+    }
+
+    @Override
+    protected void onHeightUpdated(float expandedHeight) {
+        if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
+            // Updating the clock position will set the top padding which might
+            // trigger a new panel height and re-position the clock.
+            // This is a circular dependency and should be avoided, otherwise we'll have
+            // a stack overflow.
+            if (mStackScrollerMeasuringPass > 2) {
+                if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
+            } else {
+                positionClockAndNotifications();
+            }
+        }
+        if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
+                && !mQsExpansionFromOverscroll) {
+            float t;
+            if (mKeyguardShowing) {
+
+                // On Keyguard, interpolate the QS expansion linearly to the panel expansion
+                t = expandedHeight / (getMaxPanelHeight());
+            } else {
+                // In Shade, interpolate linearly such that QS is closed whenever panel height is
+                // minimum QS expansion + minStackHeight
+                float
+                        panelHeightQsCollapsed =
+                        mNotificationStackScroller.getIntrinsicPadding()
+                                + mNotificationStackScroller.getLayoutMinHeight();
+                float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
+                t =
+                        (expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
+                                - panelHeightQsCollapsed);
+            }
+            float
+                    targetHeight =
+                    mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
+            setQsExpansion(targetHeight);
+            mHomeControlsLayout.setTranslationY(targetHeight);
+        }
+        updateExpandedHeight(expandedHeight);
+        updateHeader();
+        updateNotificationTranslucency();
+        updatePanelExpanded();
+        updateGestureExclusionRect();
+        if (DEBUG) {
+            mView.invalidate();
+        }
+    }
+
+    private void updatePanelExpanded() {
+        boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
+        if (mPanelExpanded != isExpanded) {
+            mHeadsUpManager.setIsPanelExpanded(isExpanded);
+            mStatusBar.setPanelExpanded(isExpanded);
+            mPanelExpanded = isExpanded;
+        }
+    }
+
+    private int calculatePanelHeightShade() {
+        int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+        int maxHeight = mNotificationStackScroller.getHeight() - emptyBottomMargin;
+        maxHeight += mNotificationStackScroller.getTopPaddingOverflow();
+
+        if (mBarState == StatusBarState.KEYGUARD) {
+            int
+                    minKeyguardPanelBottom =
+                    mClockPositionAlgorithm.getExpandedClockPosition()
+                            + mKeyguardStatusView.getHeight()
+                            + mNotificationStackScroller.getIntrinsicContentHeight();
+            return Math.max(maxHeight, minKeyguardPanelBottom);
+        } else {
+            return maxHeight;
+        }
+    }
+
+    private int calculatePanelHeightQsExpanded() {
+        float
+                notificationHeight =
+                mNotificationStackScroller.getHeight()
+                        - mNotificationStackScroller.getEmptyBottomMargin()
+                        - mNotificationStackScroller.getTopPadding();
+
+        // When only empty shade view is visible in QS collapsed state, simulate that we would have
+        // it in expanded QS state as well so we don't run into troubles when fading the view in/out
+        // and expanding/collapsing the whole panel from/to quick settings.
+        if (mNotificationStackScroller.getNotGoneChildCount() == 0 && mShowEmptyShadeView) {
+            notificationHeight = mNotificationStackScroller.getEmptyShadeViewHeight();
+        }
+        int maxQsHeight = mQsMaxExpansionHeight;
+
+        if (mKeyguardShowing) {
+            maxQsHeight += mQsNotificationTopPadding;
+        }
+
+        // If an animation is changing the size of the QS panel, take the animated value.
+        if (mQsSizeChangeAnimator != null) {
+            maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
+        }
+        float totalHeight = Math.max(maxQsHeight,
+                mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding
+                        : 0) + notificationHeight
+                + mNotificationStackScroller.getTopPaddingOverflow();
+        if (totalHeight > mNotificationStackScroller.getHeight()) {
+            float
+                    fullyCollapsedHeight =
+                    maxQsHeight + mNotificationStackScroller.getLayoutMinHeight();
+            totalHeight = Math.max(fullyCollapsedHeight, mNotificationStackScroller.getHeight());
+        }
+        return (int) totalHeight;
+    }
+
+    private void updateNotificationTranslucency() {
+        float alpha = 1f;
+        if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp
+                && !mHeadsUpManager.hasPinnedHeadsUp()) {
+            alpha = getFadeoutAlpha();
+        }
+        if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
+                && !mKeyguardBypassController.getBypassEnabled()) {
+            alpha *= mClockPositionResult.clockAlpha;
+        }
+        mNotificationStackScroller.setAlpha(alpha);
+    }
+
+    private float getFadeoutAlpha() {
+        float alpha;
+        if (mQsMinExpansionHeight == 0) {
+            return 1.0f;
+        }
+        alpha = getExpandedHeight() / mQsMinExpansionHeight;
+        alpha = Math.max(0, Math.min(alpha, 1));
+        alpha = (float) Math.pow(alpha, 0.75);
+        return alpha;
+    }
+
+    @Override
+    protected float getOverExpansionAmount() {
+        return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
+    }
+
+    @Override
+    protected float getOverExpansionPixels() {
+        return mNotificationStackScroller.getCurrentOverScrolledPixels(true /* top */);
+    }
+
+    /**
+     * Hides the header when notifications are colliding with it.
+     */
+    private void updateHeader() {
+        if (mBarState == StatusBarState.KEYGUARD) {
+            updateHeaderKeyguardAlpha();
+        }
+        updateQsExpansion();
+    }
+
+    protected float getHeaderTranslation() {
+        if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
+            return -mQs.getQsMinExpansionHeight();
+        }
+        float appearAmount = mNotificationStackScroller.calculateAppearFraction(mExpandedHeight);
+        float startHeight = -mQsExpansionHeight;
+        if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()
+                && mNotificationStackScroller.isPulseExpanding()) {
+            if (!mPulseExpansionHandler.isExpanding()
+                    && !mPulseExpansionHandler.getLeavingLockscreen()) {
+                // If we aborted the expansion we need to make sure the header doesn't reappear
+                // again after the header has animated away
+                appearAmount = 0;
+            } else {
+                appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
+            }
+            startHeight = -mQs.getQsMinExpansionHeight();
+            if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
+        }
+        float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount))
+                + mExpandOffset;
+        return Math.min(0, translation);
+    }
+
+    /**
+     * @return the alpha to be used to fade out the contents on Keyguard (status bar, bottom area)
+     * during swiping up
+     */
+    private float getKeyguardContentsAlpha() {
+        float alpha;
+        if (mBarState == StatusBarState.KEYGUARD) {
+
+            // When on Keyguard, we hide the header as soon as we expanded close enough to the
+            // header
+            alpha =
+                    getExpandedHeight() / (mKeyguardStatusBar.getHeight()
+                            + mNotificationsHeaderCollideDistance);
+        } else {
+
+            // In SHADE_LOCKED, the top card is already really close to the header. Hide it as
+            // soon as we start translating the stack.
+            alpha = getExpandedHeight() / mKeyguardStatusBar.getHeight();
+        }
+        alpha = MathUtils.saturate(alpha);
+        alpha = (float) Math.pow(alpha, 0.75);
+        return alpha;
+    }
+
+    private void updateHeaderKeyguardAlpha() {
+        if (!mKeyguardShowing) {
+            return;
+        }
+        float alphaQsExpansion = 1 - Math.min(1, getQsExpansionFraction() * 2);
+        float newAlpha = Math.min(getKeyguardContentsAlpha(), alphaQsExpansion)
+                * mKeyguardStatusBarAnimateAlpha;
+        newAlpha *= 1.0f - mKeyguardHeadsUpShowingAmount;
+        mKeyguardStatusBar.setAlpha(newAlpha);
+        boolean
+                hideForBypass =
+                mFirstBypassAttempt && mUpdateMonitor.shouldListenForFace()
+                        || mDelayShowingKeyguardStatusBar;
+        mKeyguardStatusBar.setVisibility(
+                newAlpha != 0f && !mDozing && !hideForBypass ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    private void updateKeyguardBottomAreaAlpha() {
+        // There are two possible panel expansion behaviors:
+        // • User dragging up to unlock: we want to fade out as quick as possible
+        //   (ALPHA_EXPANSION_THRESHOLD) to avoid seeing the bouncer over the bottom area.
+        // • User tapping on lock screen: bouncer won't be visible but panel expansion will
+        //   change due to "unlock hint animation." In this case, fading out the bottom area
+        //   would also hide the message that says "swipe to unlock," we don't want to do that.
+        float expansionAlpha = MathUtils.map(
+                isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
+                getExpandedFraction());
+        float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+        alpha *= mBottomAreaShadeAlpha;
+        mKeyguardBottomArea.setAffordanceAlpha(alpha);
+        mKeyguardBottomArea.setImportantForAccessibility(
+                alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                        : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+        View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
+        if (ambientIndicationContainer != null) {
+            ambientIndicationContainer.setAlpha(alpha);
+        }
+    }
+
+    /**
+     * Custom clock fades away when user drags up to unlock or pulls down quick settings.
+     *
+     * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
+     * {@link #updateKeyguardBottomAreaAlpha}.
+     */
+    private void updateBigClockAlpha() {
+        float expansionAlpha = MathUtils.map(
+                isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
+                getExpandedFraction());
+        float alpha = Math.min(expansionAlpha, 1 - getQsExpansionFraction());
+        mBigClockContainer.setAlpha(alpha);
+    }
+
+    @Override
+    protected void onExpandingStarted() {
+        super.onExpandingStarted();
+        mNotificationStackScroller.onExpansionStarted();
+        mIsExpanding = true;
+        mQsExpandedWhenExpandingStarted = mQsFullyExpanded;
+        if (mQsExpanded) {
+            onQsExpansionStarted();
+        }
+        // Since there are QS tiles in the header now, we need to make sure we start listening
+        // immediately so they can be up to date.
+        if (mQs == null) return;
+        mQs.setHeaderListening(true);
+    }
+
+    @Override
+    protected void onExpandingFinished() {
+        super.onExpandingFinished();
+        mNotificationStackScroller.onExpansionStopped();
+        mHeadsUpManager.onExpandingFinished();
+        mIsExpanding = false;
+        if (isFullyCollapsed()) {
+            DejankUtils.postAfterTraversal(new Runnable() {
+                @Override
+                public void run() {
+                    setListening(false);
+                }
+            });
+
+            // Workaround b/22639032: Make sure we invalidate something because else RenderThread
+            // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go
+            // ahead with rendering and we jank.
+            mView.postOnAnimation(new Runnable() {
+                @Override
+                public void run() {
+                    mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT);
+                }
+            });
+        } else {
+            setListening(true);
+        }
+        mQsExpandImmediate = false;
+        mNotificationStackScroller.setShouldShowShelfOnly(false);
+        mTwoFingerQsExpandPossible = false;
+        notifyListenersTrackingHeadsUp(null);
+        mExpandingFromHeadsUp = false;
+        setPanelScrimMinFraction(0.0f);
+    }
+
+    private void notifyListenersTrackingHeadsUp(ExpandableNotificationRow pickedChild) {
+        for (int i = 0; i < mTrackingHeadsUpListeners.size(); i++) {
+            Consumer<ExpandableNotificationRow> listener = mTrackingHeadsUpListeners.get(i);
+            listener.accept(pickedChild);
+        }
+    }
+
+    private void setListening(boolean listening) {
+        mKeyguardStatusBar.setListening(listening);
+        if (mQs == null) return;
+        mQs.setListening(listening);
+        if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
+    }
+
+    @Override
+    public void expand(boolean animate) {
+        super.expand(animate);
+        setListening(true);
+    }
+
+    @Override
+    protected void setOverExpansion(float overExpansion, boolean isPixels) {
+        if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
+            return;
+        }
+        if (mBarState != StatusBarState.KEYGUARD) {
+            mNotificationStackScroller.setOnHeightChangedListener(null);
+            if (isPixels) {
+                mNotificationStackScroller.setOverScrolledPixels(overExpansion, true /* onTop */,
+                        false /* animate */);
+            } else {
+                mNotificationStackScroller.setOverScrollAmount(overExpansion, true /* onTop */,
+                        false /* animate */);
+            }
+            mNotificationStackScroller.setOnHeightChangedListener(mOnHeightChangedListener);
+        }
+    }
+
+    @Override
+    protected void onTrackingStarted() {
+        mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen());
+        super.onTrackingStarted();
+        if (mQsFullyExpanded) {
+            mQsExpandImmediate = true;
+            mNotificationStackScroller.setShouldShowShelfOnly(true);
+        }
+        if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
+            mAffordanceHelper.animateHideLeftRightIcon();
+        }
+        mNotificationStackScroller.onPanelTrackingStarted();
+    }
+
+    @Override
+    protected void onTrackingStopped(boolean expand) {
+        mFalsingManager.onTrackingStopped();
+        super.onTrackingStopped(expand);
+        if (expand) {
+            mNotificationStackScroller.setOverScrolledPixels(0.0f, true /* onTop */,
+                    true /* animate */);
+        }
+        mNotificationStackScroller.onPanelTrackingStopped();
+        if (expand && (mBarState == StatusBarState.KEYGUARD
+                || mBarState == StatusBarState.SHADE_LOCKED)) {
+            if (!mHintAnimationRunning) {
+                mAffordanceHelper.reset(true);
+            }
+        }
+    }
+
+    private void updateMaxHeadsUpTranslation() {
+        mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
+    }
+
+    @Override
+    protected void startUnlockHintAnimation() {
+        if (mPowerManager.isPowerSaveMode()) {
+            onUnlockHintStarted();
+            onUnlockHintFinished();
+            return;
+        }
+        super.startUnlockHintAnimation();
+    }
+
+    @Override
+    protected void onUnlockHintFinished() {
+        super.onUnlockHintFinished();
+        mNotificationStackScroller.setUnlockHintRunning(false);
+    }
+
+    @Override
+    protected void onUnlockHintStarted() {
+        super.onUnlockHintStarted();
+        mNotificationStackScroller.setUnlockHintRunning(true);
+    }
+
+    @Override
+    protected float getPeekHeight() {
+        if (mNotificationStackScroller.getNotGoneChildCount() > 0) {
+            return mNotificationStackScroller.getPeekHeight();
+        } else {
+            return mQsMinExpansionHeight;
+        }
+    }
+
+    @Override
+    protected boolean shouldUseDismissingAnimation() {
+        return mBarState != StatusBarState.SHADE && (mKeyguardStateController.canDismissLockScreen()
+                || !isTracking());
+    }
+
+    @Override
+    protected boolean fullyExpandedClearAllVisible() {
+        return mNotificationStackScroller.isFooterViewNotGone()
+                && mNotificationStackScroller.isScrolledToBottom() && !mQsExpandImmediate;
+    }
+
+    @Override
+    protected boolean isClearAllVisible() {
+        return mNotificationStackScroller.isFooterViewContentVisible();
+    }
+
+    @Override
+    protected int getClearAllHeight() {
+        return mNotificationStackScroller.getFooterViewHeight();
+    }
+
+    @Override
+    protected boolean isTrackingBlocked() {
+        return mConflictingQsExpansionGesture && mQsExpanded || mBlockingExpansionForCurrentTouch;
+    }
+
+    public boolean isQsExpanded() {
+        return mQsExpanded;
+    }
+
+    public boolean isQsDetailShowing() {
+        return mQs.isShowingDetail();
+    }
+
+    public void closeQsDetail() {
+        mQs.closeDetail();
+    }
+
+    public boolean isLaunchTransitionFinished() {
+        return mIsLaunchTransitionFinished;
+    }
+
+    public boolean isLaunchTransitionRunning() {
+        return mIsLaunchTransitionRunning;
+    }
+
+    public void setLaunchTransitionEndRunnable(Runnable r) {
+        mLaunchAnimationEndRunnable = r;
+    }
+
+    private void updateDozingVisibilities(boolean animate) {
+        mKeyguardBottomArea.setDozing(mDozing, animate);
+        if (!mDozing && animate) {
+            animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        }
+    }
+
+    @Override
+    public boolean isDozing() {
+        return mDozing;
+    }
+
+    public void showEmptyShadeView(boolean emptyShadeViewVisible) {
+        mShowEmptyShadeView = emptyShadeViewVisible;
+        updateEmptyShadeView();
+    }
+
+    private void updateEmptyShadeView() {
+        // Hide "No notifications" in QS.
+        mNotificationStackScroller.updateEmptyShadeView(mShowEmptyShadeView && !mQsExpanded);
+    }
+
+    public void setQsScrimEnabled(boolean qsScrimEnabled) {
+        boolean changed = mQsScrimEnabled != qsScrimEnabled;
+        mQsScrimEnabled = qsScrimEnabled;
+        if (changed) {
+            updateQsState();
+        }
+    }
+
+    public void setKeyguardUserSwitcher(KeyguardUserSwitcher keyguardUserSwitcher) {
+        mKeyguardUserSwitcher = keyguardUserSwitcher;
+    }
+
+    public void onScreenTurningOn() {
+        mKeyguardStatusView.dozeTimeTick();
+    }
+
+    @Override
+    protected boolean onMiddleClicked() {
+        switch (mBarState) {
+            case StatusBarState.KEYGUARD:
+                if (!mDozingOnDown) {
+                    if (mKeyguardBypassController.getBypassEnabled()) {
+                        mUpdateMonitor.requestFaceAuth();
+                    } else {
+                        mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT,
+                                0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
+                        startUnlockHintAnimation();
+                    }
+                }
+                return true;
+            case StatusBarState.SHADE_LOCKED:
+                if (!mQsExpanded) {
+                    mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+                }
+                return true;
+            case StatusBarState.SHADE:
+
+                // This gets called in the middle of the touch handling, where the state is still
+                // that we are tracking the panel. Collapse the panel after this is done.
+                mView.post(mPostCollapseRunnable);
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    public void setPanelAlpha(int alpha, boolean animate) {
+        if (mPanelAlpha != alpha) {
+            mPanelAlpha = alpha;
+            PropertyAnimator.setProperty(mView, mPanelAlphaAnimator, alpha, alpha == 255
+                            ? mPanelAlphaInPropertiesAnimator : mPanelAlphaOutPropertiesAnimator,
+                    animate);
+        }
+    }
+
+    public void setPanelAlphaEndAction(Runnable r) {
+        mPanelAlphaEndAction = r;
+    }
+
+    private void updateKeyguardStatusBarForHeadsUp() {
+        boolean
+                showingKeyguardHeadsUp =
+                mKeyguardShowing && mHeadsUpAppearanceController.shouldBeVisible();
+        if (mShowingKeyguardHeadsUp != showingKeyguardHeadsUp) {
+            mShowingKeyguardHeadsUp = showingKeyguardHeadsUp;
+            if (mKeyguardShowing) {
+                PropertyAnimator.setProperty(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT,
+                        showingKeyguardHeadsUp ? 1.0f : 0.0f, KEYGUARD_HUN_PROPERTIES,
+                        true /* animate */);
+            } else {
+                PropertyAnimator.applyImmediately(mView, KEYGUARD_HEADS_UP_SHOWING_AMOUNT, 0.0f);
+            }
+        }
+    }
+
+    private void setKeyguardHeadsUpShowingAmount(float amount) {
+        mKeyguardHeadsUpShowingAmount = amount;
+        updateHeaderKeyguardAlpha();
+    }
+
+    private float getKeyguardHeadsUpShowingAmount() {
+        return mKeyguardHeadsUpShowingAmount;
+    }
+
+    public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
+        mHeadsUpAnimatingAway = headsUpAnimatingAway;
+        mNotificationStackScroller.setHeadsUpAnimatingAway(headsUpAnimatingAway);
+        updateHeadsUpVisibility();
+    }
+
+    private void updateHeadsUpVisibility() {
+        ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
+    }
+
+    @Override
+    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+        super.setHeadsUpManager(headsUpManager);
+        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
+                mNotificationStackScroller.getHeadsUpCallback(),
+                NotificationPanelViewController.this);
+    }
+
+    public void setTrackedHeadsUp(ExpandableNotificationRow pickedChild) {
+        if (pickedChild != null) {
+            notifyListenersTrackingHeadsUp(pickedChild);
+            mExpandingFromHeadsUp = true;
+        }
+        // otherwise we update the state when the expansion is finished
+    }
+
+    @Override
+    protected void onClosingFinished() {
+        super.onClosingFinished();
+        resetHorizontalPanelPosition();
+        setClosingWithAlphaFadeout(false);
+    }
+
+    private void setClosingWithAlphaFadeout(boolean closing) {
+        mClosingWithAlphaFadeOut = closing;
+        mNotificationStackScroller.forceNoOverlappingRendering(closing);
+    }
+
+    /**
+     * Updates the vertical position of the panel so it is positioned closer to the touch
+     * responsible for opening the panel.
+     *
+     * @param x the x-coordinate the touch event
+     */
+    protected void updateVerticalPanelPosition(float x) {
+        if (mNotificationStackScroller.getWidth() * 1.75f > mView.getWidth()) {
+            resetHorizontalPanelPosition();
+            return;
+        }
+        float leftMost = mPositionMinSideMargin + mNotificationStackScroller.getWidth() / 2;
+        float
+                rightMost =
+                mView.getWidth() - mPositionMinSideMargin
+                        - mNotificationStackScroller.getWidth() / 2;
+        if (Math.abs(x - mView.getWidth() / 2) < mNotificationStackScroller.getWidth() / 4) {
+            x = mView.getWidth() / 2;
+        }
+        x = Math.min(rightMost, Math.max(leftMost, x));
+        float
+                center =
+                mNotificationStackScroller.getLeft() + mNotificationStackScroller.getWidth() / 2;
+        setHorizontalPanelTranslation(x - center);
+    }
+
+    private void resetHorizontalPanelPosition() {
+        setHorizontalPanelTranslation(0f);
+    }
+
+    protected void setHorizontalPanelTranslation(float translation) {
+        mNotificationStackScroller.setTranslationX(translation);
+        mQsFrame.setTranslationX(translation);
+        int size = mVerticalTranslationListener.size();
+        for (int i = 0; i < size; i++) {
+            mVerticalTranslationListener.get(i).run();
+        }
+    }
+
+    protected void updateExpandedHeight(float expandedHeight) {
+        if (mTracking) {
+            mNotificationStackScroller.setExpandingVelocity(getCurrentExpandVelocity());
+        }
+        if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard()) {
+            // The expandedHeight is always the full panel Height when bypassing
+            expandedHeight = getMaxPanelHeightNonBypass();
+        }
+        mNotificationStackScroller.setExpandedHeight(expandedHeight);
+        updateKeyguardBottomAreaAlpha();
+        updateBigClockAlpha();
+        updateStatusBarIcons();
+    }
+
+    /**
+     * @return whether the notifications are displayed full width and don't have any margins on
+     * the side.
+     */
+    public boolean isFullWidth() {
+        return mIsFullWidth;
+    }
+
+    private void updateStatusBarIcons() {
+        boolean
+                showIconsWhenExpanded =
+                (isPanelVisibleBecauseOfHeadsUp() || isFullWidth())
+                        && getExpandedHeight() < getOpeningHeight();
+        boolean noVisibleNotifications = true;
+        if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) {
+            showIconsWhenExpanded = false;
+        }
+        if (showIconsWhenExpanded != mShowIconsWhenExpanded) {
+            mShowIconsWhenExpanded = showIconsWhenExpanded;
+            mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+        }
+    }
+
+    private boolean isOnKeyguard() {
+        return mBarState == StatusBarState.KEYGUARD;
+    }
+
+    public void setPanelScrimMinFraction(float minFraction) {
+        mBar.panelScrimMinFractionChanged(minFraction);
+    }
+
+    public void clearNotificationEffects() {
+        mStatusBar.clearNotificationEffects();
+    }
+
+    @Override
+    protected boolean isPanelVisibleBecauseOfHeadsUp() {
+        return (mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway)
+                && mBarState == StatusBarState.SHADE;
+    }
+
+    public void launchCamera(boolean animate, int source) {
+        if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP;
+        } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE) {
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_WIGGLE;
+        } else if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER) {
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER;
+        } else {
+
+            // Default.
+            mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
+        }
+
+        // If we are launching it when we are occluded already we don't want it to animate,
+        // nor setting these flags, since the occluded state doesn't change anymore, hence it's
+        // never reset.
+        if (!isFullyCollapsed()) {
+            setLaunchingAffordance(true);
+        } else {
+            animate = false;
+        }
+        mAffordanceHasPreview = mKeyguardBottomArea.getRightPreview() != null;
+        mAffordanceHelper.launchAffordance(
+                animate, mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+    }
+
+    public void onAffordanceLaunchEnded() {
+        setLaunchingAffordance(false);
+    }
+
+    /**
+     * Set whether we are currently launching an affordance. This is currently only set when
+     * launched via a camera gesture.
+     */
+    private void setLaunchingAffordance(boolean launchingAffordance) {
+        mLaunchingAffordance = launchingAffordance;
+        mKeyguardAffordanceHelperCallback.getLeftIcon().setLaunchingAffordance(launchingAffordance);
+        mKeyguardAffordanceHelperCallback.getRightIcon().setLaunchingAffordance(
+                launchingAffordance);
+        mKeyguardBypassController.setLaunchingAffordance(launchingAffordance);
+        if (mAffordanceLaunchListener != null) {
+            mAffordanceLaunchListener.accept(launchingAffordance);
+        }
+    }
+
+    /**
+     * Return true when a bottom affordance is launching an occluded activity with a splash screen.
+     */
+    public boolean isLaunchingAffordanceWithPreview() {
+        return mLaunchingAffordance && mAffordanceHasPreview;
+    }
+
+    /**
+     * Whether the camera application can be launched for the camera launch gesture.
+     */
+    public boolean canCameraGestureBeLaunched() {
+        if (!mStatusBar.isCameraAllowedByAdmin()) {
+            return false;
+        }
+
+        ResolveInfo resolveInfo = mKeyguardBottomArea.resolveCameraIntent();
+        String
+                packageToLaunch =
+                (resolveInfo == null || resolveInfo.activityInfo == null) ? null
+                        : resolveInfo.activityInfo.packageName;
+        return packageToLaunch != null && (mBarState != StatusBarState.SHADE || !isForegroundApp(
+                packageToLaunch)) && !mAffordanceHelper.isSwipingInProgress();
+    }
+
+    /**
+     * Return true if the applications with the package name is running in foreground.
+     *
+     * @param pkgName application package name.
+     */
+    private boolean isForegroundApp(String pkgName) {
+        List<ActivityManager.RunningTaskInfo> tasks = mActivityManager.getRunningTasks(1);
+        return !tasks.isEmpty() && pkgName.equals(tasks.get(0).topActivity.getPackageName());
+    }
+
+    private void setGroupManager(NotificationGroupManager groupManager) {
+        mGroupManager = groupManager;
+    }
+
+    public boolean hideStatusBarIconsWhenExpanded() {
+        if (mLaunchingNotification) {
+            return mHideIconsDuringNotificationLaunch;
+        }
+        if (mHeadsUpAppearanceController != null
+                && mHeadsUpAppearanceController.shouldBeVisible()) {
+            return false;
+        }
+        return !isFullWidth() || !mShowIconsWhenExpanded;
+    }
+
+    private final FragmentListener mFragmentListener = new FragmentListener() {
+        @Override
+        public void onFragmentViewCreated(String tag, Fragment fragment) {
+            mQs = (QS) fragment;
+            mQs.setPanelView(mHeightListener);
+            mQs.setExpandClickListener(mOnClickListener);
+            mQs.setHeaderClickable(mQsExpansionEnabled);
+            updateQSPulseExpansion();
+            mQs.setOverscrolling(mStackScrollerOverscrolling);
+
+            // recompute internal state when qspanel height changes
+            mQs.getView().addOnLayoutChangeListener(
+                    (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                        final int height = bottom - top;
+                        final int oldHeight = oldBottom - oldTop;
+                        if (height != oldHeight) {
+                            mHeightListener.onQsHeightChanged();
+                        }
+                    });
+            mNotificationStackScroller.setQsContainer((ViewGroup) mQs.getView());
+            if (mQs instanceof QSFragment) {
+                mKeyguardStatusBar.setQSPanel(((QSFragment) mQs).getQsPanel());
+            }
+            updateQsExpansion();
+        }
+
+        @Override
+        public void onFragmentViewDestroyed(String tag, Fragment fragment) {
+            // Manual handling of fragment lifecycle is only required because this bridges
+            // non-fragment and fragment code. Once we are using a fragment for the notification
+            // panel, mQs will not need to be null cause it will be tied to the same lifecycle.
+            if (fragment == mQs) {
+                mQs = null;
+            }
+        }
+    };
+
+    @Override
+    public void setTouchAndAnimationDisabled(boolean disabled) {
+        super.setTouchAndAnimationDisabled(disabled);
+        if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
+            mAffordanceHelper.reset(false /* animate */);
+        }
+        mNotificationStackScroller.setAnimationsEnabled(!disabled);
+    }
+
+    /**
+     * Sets the dozing state.
+     *
+     * @param dozing              {@code true} when dozing.
+     * @param animate             if transition should be animated.
+     * @param wakeUpTouchLocation touch event location - if woken up by SLPI sensor.
+     */
+    public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation) {
+        if (dozing == mDozing) return;
+        mView.setDozing(dozing);
+        mDozing = dozing;
+        mNotificationStackScroller.setDozing(mDozing, animate, wakeUpTouchLocation);
+        mKeyguardBottomArea.setDozing(mDozing, animate);
+
+        if (dozing) {
+            mBottomAreaShadeAlphaAnimator.cancel();
+        }
+
+        if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
+            updateDozingVisibilities(animate);
+        }
+
+        final float dozeAmount = dozing ? 1 : 0;
+        mStatusBarStateController.setDozeAmount(dozeAmount, animate);
+    }
+
+    public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
+        final boolean
+                animatePulse =
+                !mDozeParameters.getDisplayNeedsBlanking() && mDozeParameters.getAlwaysOn();
+        if (animatePulse) {
+            mAnimateNextPositionUpdate = true;
+        }
+        // Do not animate the clock when waking up from a pulse.
+        // The height callback will take care of pushing the clock to the right position.
+        if (!mPulsing && !mDozing) {
+            mAnimateNextPositionUpdate = false;
+        }
+        mNotificationStackScroller.setPulsing(pulsing, animatePulse);
+        mKeyguardStatusView.setPulsing(pulsing);
+    }
+
+    public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
+        if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
+            mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
+            mStatusBar.updateKeyguardMaxNotifications();
+        }
+    }
+
+    public void dozeTimeTick() {
+        mKeyguardBottomArea.dozeTimeTick();
+        mKeyguardStatusView.dozeTimeTick();
+        if (mInterpolatedDarkAmount > 0) {
+            positionClockAndNotifications();
+        }
+    }
+
+    public void setStatusAccessibilityImportance(int mode) {
+        mKeyguardStatusView.setImportantForAccessibility(mode);
+    }
+
+    /**
+     * TODO: this should be removed.
+     * It's not correct to pass this view forward because other classes will end up adding
+     * children to it. Theme will be out of sync.
+     *
+     * @return bottom area view
+     */
+    public KeyguardBottomAreaView getKeyguardBottomAreaView() {
+        return mKeyguardBottomArea;
+    }
+
+    public void setUserSetupComplete(boolean userSetupComplete) {
+        mUserSetupComplete = userSetupComplete;
+        mKeyguardBottomArea.setUserSetupComplete(userSetupComplete);
+    }
+
+    public void applyExpandAnimationParams(ExpandAnimationParameters params) {
+        mExpandOffset = params != null ? params.getTopChange() : 0;
+        updateQsExpansion();
+        if (params != null) {
+            boolean hideIcons = params.getProgress(
+                    ActivityLaunchAnimator.ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
+            if (hideIcons != mHideIconsDuringNotificationLaunch) {
+                mHideIconsDuringNotificationLaunch = hideIcons;
+                if (!hideIcons) {
+                    mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
+                }
+            }
+        }
+    }
+
+    public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+        mTrackingHeadsUpListeners.add(listener);
+    }
+
+    public void removeTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
+        mTrackingHeadsUpListeners.remove(listener);
+    }
+
+    public void addVerticalTranslationListener(Runnable verticalTranslationListener) {
+        mVerticalTranslationListener.add(verticalTranslationListener);
+    }
+
+    public void removeVerticalTranslationListener(Runnable verticalTranslationListener) {
+        mVerticalTranslationListener.remove(verticalTranslationListener);
+    }
+
+    public void setHeadsUpAppearanceController(
+            HeadsUpAppearanceController headsUpAppearanceController) {
+        mHeadsUpAppearanceController = headsUpAppearanceController;
+    }
+
+    /**
+     * Starts the animation before we dismiss Keyguard, i.e. an disappearing animation on the
+     * security view of the bouncer.
+     */
+    public void onBouncerPreHideAnimation() {
+        setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */,
+                false /* goingToFullShade */);
+    }
+
+    /**
+     * Do not let the user drag the shade up and down for the current touch session.
+     * This is necessary to avoid shade expansion while/after the bouncer is dismissed.
+     */
+    public void blockExpansionForCurrentTouch() {
+        mBlockingExpansionForCurrentTouch = mTracking;
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        super.dump(fd, pw, args);
+        pw.println("    gestureExclusionRect: " + calculateGestureExclusionRect());
+        if (mKeyguardStatusBar != null) {
+            mKeyguardStatusBar.dump(fd, pw, args);
+        }
+        if (mKeyguardStatusView != null) {
+            mKeyguardStatusView.dump(fd, pw, args);
+        }
+    }
+
+    public boolean hasActiveClearableNotifications() {
+        return mNotificationStackScroller.hasActiveClearableNotifications(ROWS_ALL);
+    }
+
+    private void updateShowEmptyShadeView() {
+        boolean
+                showEmptyShadeView =
+                mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
+        showEmptyShadeView(showEmptyShadeView);
+    }
+
+    public RemoteInputController.Delegate createRemoteInputDelegate() {
+        return mNotificationStackScroller.createDelegate();
+    }
+
+    public void updateNotificationViews() {
+        mNotificationStackScroller.updateSectionBoundaries();
+        mNotificationStackScroller.updateSpeedBumpIndex();
+        mNotificationStackScroller.updateFooter();
+        updateShowEmptyShadeView();
+        mNotificationStackScroller.updateIconAreaViews();
+    }
+
+    public void onUpdateRowStates() {
+        mNotificationStackScroller.onUpdateRowStates();
+    }
+
+    public boolean hasPulsingNotifications() {
+        return mNotificationStackScroller.hasPulsingNotifications();
+    }
+
+    public ActivatableNotificationView getActivatedChild() {
+        return mNotificationStackScroller.getActivatedChild();
+    }
+
+    public void setActivatedChild(ActivatableNotificationView o) {
+        mNotificationStackScroller.setActivatedChild(o);
+    }
+
+    public void runAfterAnimationFinished(Runnable r) {
+        mNotificationStackScroller.runAfterAnimationFinished(r);
+    }
+
+    public void setScrollingEnabled(boolean b) {
+        mNotificationStackScroller.setScrollingEnabled(b);
+    }
+
+    public void initDependencies(StatusBar statusBar, NotificationGroupManager groupManager,
+            NotificationShelf notificationShelf,
+            NotificationIconAreaController notificationIconAreaController,
+            ScrimController scrimController) {
+        setStatusBar(statusBar);
+        setGroupManager(mGroupManager);
+        mNotificationStackScroller.setNotificationPanelController(this);
+        mNotificationStackScroller.setIconAreaController(notificationIconAreaController);
+        mNotificationStackScroller.setStatusBar(statusBar);
+        mNotificationStackScroller.setGroupManager(groupManager);
+        mNotificationStackScroller.setShelf(notificationShelf);
+        mNotificationStackScroller.setScrimController(scrimController);
+        updateShowEmptyShadeView();
+    }
+
+    public void showTransientIndication(int id) {
+        mKeyguardIndicationController.showTransientIndication(id);
+    }
+
+    public void setOnReinflationListener(Runnable onReinflationListener) {
+        mOnReinflationListener = onReinflationListener;
+    }
+
+    public static boolean isQsSplitEnabled() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.QS_SPLIT_ENABLED, false);
+    }
+
+    public void setAlpha(float alpha) {
+        mView.setAlpha(alpha);
+    }
+
+    public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
+        return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
+                durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
+                endAction);
+    }
+
+    public void resetViewGroupFade() {
+        ViewGroupFadeHelper.reset(mView);
+    }
+
+    public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+        mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
+    }
+
+    public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+        mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
+    }
+
+    public MyOnHeadsUpChangedListener getOnHeadsUpChangedListener() {
+        return mOnHeadsUpChangedListener;
+    }
+
+    public int getHeight() {
+        return mView.getHeight();
+    }
+
+    public TextView getHeaderDebugInfo() {
+        return mView.findViewById(R.id.header_debug_info);
+    }
+
+    public void onThemeChanged() {
+        mConfigurationListener.onThemeChanged();
+    }
+
+    @Override
+    public OnLayoutChangeListener createLayoutChangeListener() {
+        return new OnLayoutChangeListener();
+    }
+
+    public void setEmptyDragAmount(float amount) {
+        mExpansionCallback.setEmptyDragAmount(amount);
+    }
+
+    @Override
+    protected TouchHandler createTouchHandler() {
+        return new TouchHandler() {
+            @Override
+            public boolean onInterceptTouchEvent(MotionEvent event) {
+                if (mBlockTouches || mQsFullyExpanded && mQs.onInterceptTouchEvent(event)) {
+                    return false;
+                }
+                initDownStates(event);
+                // Do not let touches go to shade or QS if the bouncer is visible,
+                // but still let user swipe down to expand the panel, dismissing the bouncer.
+                if (mStatusBar.isBouncerShowing()) {
+                    return true;
+                }
+                if (mBar.panelEnabled() && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+                    mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
+                    mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
+                    return true;
+                }
+                if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
+                        && mPulseExpansionHandler.onInterceptTouchEvent(event)) {
+                    return true;
+                }
+
+                if (!isFullyCollapsed() && onQsIntercept(event)) {
+                    return true;
+                }
+                return super.onInterceptTouchEvent(event);
+            }
+
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                if (mBlockTouches || (mQs != null && mQs.isCustomizing())) {
+                    return false;
+                }
+
+                // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able
+                // to pull down QS or expand the shade.
+                if (mStatusBar.isBouncerShowingScrimmed()) {
+                    return false;
+                }
+
+                // Make sure the next touch won't the blocked after the current ends.
+                if (event.getAction() == MotionEvent.ACTION_UP
+                        || event.getAction() == MotionEvent.ACTION_CANCEL) {
+                    mBlockingExpansionForCurrentTouch = false;
+                }
+                // When touch focus transfer happens, ACTION_DOWN->ACTION_UP may happen immediately
+                // without any ACTION_MOVE event.
+                // In such case, simply expand the panel instead of being stuck at the bottom bar.
+                if (mLastEventSynthesizedDown && event.getAction() == MotionEvent.ACTION_UP) {
+                    expand(true /* animate */);
+                }
+                initDownStates(event);
+                if (!mIsExpanding && !shouldQuickSettingsIntercept(mDownX, mDownY, 0)
+                        && mPulseExpansionHandler.onTouchEvent(event)) {
+                    // We're expanding all the other ones shouldn't get this anymore
+                    return true;
+                }
+                if (mListenForHeadsUp && !mHeadsUpTouchHelper.isTrackingHeadsUp()
+                        && mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
+                    mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
+                }
+                boolean handled = false;
+                if ((!mIsExpanding || mHintAnimationRunning) && !mQsExpanded
+                        && mBarState != StatusBarState.SHADE && !mDozing) {
+                    handled |= mAffordanceHelper.onTouchEvent(event);
+                }
+                if (mOnlyAffordanceInThisMotion) {
+                    return true;
+                }
+                handled |= mHeadsUpTouchHelper.onTouchEvent(event);
+
+                if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
+                    return true;
+                }
+                if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
+                    mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
+                    updateVerticalPanelPosition(event.getX());
+                    handled = true;
+                }
+                handled |= super.onTouch(v, event);
+                return !mDozing || mPulsing || handled;
+            }
+        };
+    }
+
+    @Override
+    protected PanelViewController.OnConfigurationChangedListener
+            createOnConfigurationChangedListener() {
+        return new OnConfigurationChangedListener();
+    }
+
+    private class OnHeightChangedListener implements ExpandableView.OnHeightChangedListener {
+        @Override
+        public void onHeightChanged(ExpandableView view, boolean needsAnimation) {
+
+            // Block update if we are in quick settings and just the top padding changed
+            // (i.e. view == null).
+            if (view == null && mQsExpanded) {
+                return;
+            }
+            if (needsAnimation && mInterpolatedDarkAmount == 0) {
+                mAnimateNextPositionUpdate = true;
+            }
+            ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
+            ExpandableNotificationRow
+                    firstRow =
+                    firstChildNotGone instanceof ExpandableNotificationRow
+                            ? (ExpandableNotificationRow) firstChildNotGone : null;
+            if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
+                    == firstRow))) {
+                requestScrollerTopPaddingUpdate(false /* animate */);
+            }
+            requestPanelHeightUpdate();
+        }
+
+        @Override
+        public void onReset(ExpandableView view) {
+        }
+    }
+
+    private class OnClickListener implements View.OnClickListener {
+        @Override
+        public void onClick(View v) {
+            onQsExpansionStarted();
+            if (mQsExpanded) {
+                flingSettings(0 /* vel */, FLING_COLLAPSE, null /* onFinishRunnable */,
+                        true /* isClick */);
+            } else if (mQsExpansionEnabled) {
+                mLockscreenGestureLogger.write(MetricsEvent.ACTION_SHADE_QS_TAP, 0, 0);
+                flingSettings(0 /* vel */, FLING_EXPAND, null /* onFinishRunnable */,
+                        true /* isClick */);
+            }
+        }
+    }
+
+    private class OnOverscrollTopChangedListener implements
+            NotificationStackScrollLayout.OnOverscrollTopChangedListener {
+        @Override
+        public void onOverscrollTopChanged(float amount, boolean isRubberbanded) {
+            cancelQsAnimation();
+            if (!mQsExpansionEnabled) {
+                amount = 0f;
+            }
+            float rounded = amount >= 1f ? amount : 0f;
+            setOverScrolling(rounded != 0f && isRubberbanded);
+            mQsExpansionFromOverscroll = rounded != 0f;
+            mLastOverscroll = rounded;
+            updateQsState();
+            setQsExpansion(mQsMinExpansionHeight + rounded);
+        }
+
+        @Override
+        public void flingTopOverscroll(float velocity, boolean open) {
+            mLastOverscroll = 0f;
+            mQsExpansionFromOverscroll = false;
+            setQsExpansion(mQsExpansionHeight);
+            flingSettings(!mQsExpansionEnabled && open ? 0f : velocity,
+                    open && mQsExpansionEnabled ? FLING_EXPAND : FLING_COLLAPSE, () -> {
+                        mStackScrollerOverscrolling = false;
+                        setOverScrolling(false);
+                        updateQsState();
+                    }, false /* isClick */);
+        }
+    }
+
+    private class DynamicPrivacyControlListener implements DynamicPrivacyController.Listener {
+        @Override
+        public void onDynamicPrivacyChanged() {
+            // Do not request animation when pulsing or waking up, otherwise the clock wiill be out
+            // of sync with the notification panel.
+            if (mLinearDarkAmount != 0) {
+                return;
+            }
+            mAnimateNextPositionUpdate = true;
+        }
+    }
+
+    private class KeyguardAffordanceHelperCallback implements KeyguardAffordanceHelper.Callback {
+        @Override
+        public void onAnimationToSideStarted(boolean rightPage, float translation, float vel) {
+            boolean
+                    start =
+                    mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? rightPage
+                            : !rightPage;
+            mIsLaunchTransitionRunning = true;
+            mLaunchAnimationEndRunnable = null;
+            float displayDensity = mStatusBar.getDisplayDensity();
+            int lengthDp = Math.abs((int) (translation / displayDensity));
+            int velocityDp = Math.abs((int) (vel / displayDensity));
+            if (start) {
+                mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp);
+
+                mFalsingManager.onLeftAffordanceOn();
+                if (mFalsingManager.shouldEnforceBouncer()) {
+                    mStatusBar.executeRunnableDismissingKeyguard(
+                            () -> mKeyguardBottomArea.launchLeftAffordance(), null,
+                            true /* dismissShade */, false /* afterKeyguardGone */,
+                            true /* deferred */);
+                } else {
+                    mKeyguardBottomArea.launchLeftAffordance();
+                }
+            } else {
+                if (KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE.equals(
+                        mLastCameraLaunchSource)) {
+                    mLockscreenGestureLogger.write(
+                            MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp);
+                }
+                mFalsingManager.onCameraOn();
+                if (mFalsingManager.shouldEnforceBouncer()) {
+                    mStatusBar.executeRunnableDismissingKeyguard(
+                            () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
+                            true /* dismissShade */, false /* afterKeyguardGone */,
+                            true /* deferred */);
+                } else {
+                    mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
+                }
+            }
+            mStatusBar.startLaunchTransitionTimeout();
+            mBlockTouches = true;
+        }
+
+        @Override
+        public void onAnimationToSideEnded() {
+            mIsLaunchTransitionRunning = false;
+            mIsLaunchTransitionFinished = true;
+            if (mLaunchAnimationEndRunnable != null) {
+                mLaunchAnimationEndRunnable.run();
+                mLaunchAnimationEndRunnable = null;
+            }
+            mStatusBar.readyForKeyguardDone();
+        }
+
+        @Override
+        public float getMaxTranslationDistance() {
+            return (float) Math.hypot(mView.getWidth(), getHeight());
+        }
+
+        @Override
+        public void onSwipingStarted(boolean rightIcon) {
+            mFalsingManager.onAffordanceSwipingStarted(rightIcon);
+            boolean
+                    camera =
+                    mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
+                            : rightIcon;
+            if (camera) {
+                mKeyguardBottomArea.bindCameraPrewarmService();
+            }
+            mView.requestDisallowInterceptTouchEvent(true);
+            mOnlyAffordanceInThisMotion = true;
+            mQsTracking = false;
+        }
+
+        @Override
+        public void onSwipingAborted() {
+            mFalsingManager.onAffordanceSwipingAborted();
+            mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */);
+        }
+
+        @Override
+        public void onIconClicked(boolean rightIcon) {
+            if (mHintAnimationRunning) {
+                return;
+            }
+            mHintAnimationRunning = true;
+            mAffordanceHelper.startHintAnimation(rightIcon, () -> {
+                mHintAnimationRunning = false;
+                mStatusBar.onHintFinished();
+            });
+            rightIcon =
+                    mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
+                            : rightIcon;
+            if (rightIcon) {
+                mStatusBar.onCameraHintStarted();
+            } else {
+                if (mKeyguardBottomArea.isLeftVoiceAssist()) {
+                    mStatusBar.onVoiceAssistHintStarted();
+                } else {
+                    mStatusBar.onPhoneHintStarted();
+                }
+            }
+        }
+
+        @Override
+        public KeyguardAffordanceView getLeftIcon() {
+            return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                    ? mKeyguardBottomArea.getRightView() : mKeyguardBottomArea.getLeftView();
+        }
+
+        @Override
+        public KeyguardAffordanceView getRightIcon() {
+            return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                    ? mKeyguardBottomArea.getLeftView() : mKeyguardBottomArea.getRightView();
+        }
+
+        @Override
+        public View getLeftPreview() {
+            return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                    ? mKeyguardBottomArea.getRightPreview() : mKeyguardBottomArea.getLeftPreview();
+        }
+
+        @Override
+        public View getRightPreview() {
+            return mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
+                    ? mKeyguardBottomArea.getLeftPreview() : mKeyguardBottomArea.getRightPreview();
+        }
+
+        @Override
+        public float getAffordanceFalsingFactor() {
+            return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+        }
+
+        @Override
+        public boolean needsAntiFalsing() {
+            return mBarState == StatusBarState.KEYGUARD;
+        }
+    }
+
+    private class OnEmptySpaceClickListener implements
+            NotificationStackScrollLayout.OnEmptySpaceClickListener {
+        @Override
+        public void onEmptySpaceClicked(float x, float y) {
+            onEmptySpaceClick(x);
+        }
+    }
+
+    private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
+        @Override
+        public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
+            mNotificationStackScroller.setInHeadsUpPinnedMode(inPinnedMode);
+            if (inPinnedMode) {
+                mHeadsUpExistenceChangedRunnable.run();
+                updateNotificationTranslucency();
+            } else {
+                setHeadsUpAnimatingAway(true);
+                mNotificationStackScroller.runAfterAnimationFinished(
+                        mHeadsUpExistenceChangedRunnable);
+            }
+            updateGestureExclusionRect();
+            mHeadsUpPinnedMode = inPinnedMode;
+            updateHeadsUpVisibility();
+            updateKeyguardStatusBarForHeadsUp();
+        }
+
+        @Override
+        public void onHeadsUpPinned(NotificationEntry entry) {
+            if (!isOnKeyguard()) {
+                mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(),
+                        true);
+            }
+        }
+
+        @Override
+        public void onHeadsUpUnPinned(NotificationEntry entry) {
+
+            // When we're unpinning the notification via active edge they remain heads-upped,
+            // we need to make sure that an animation happens in this case, otherwise the
+            // notification
+            // will stick to the top without any interaction.
+            if (isFullyCollapsed() && entry.isRowHeadsUp() && !isOnKeyguard()) {
+                mNotificationStackScroller.generateHeadsUpAnimation(
+                        entry.getHeadsUpAnimationView(), false);
+                entry.setHeadsUpIsVisible();
+            }
+        }
+
+        @Override
+        public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+            mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
+        }
+    }
+
+    private class HeightListener implements QS.HeightListener {
+        public void onQsHeightChanged() {
+            mQsMaxExpansionHeight = mQs != null ? mQs.getDesiredHeight() : 0;
+            if (mQsExpanded && mQsFullyExpanded) {
+                mQsExpansionHeight = mQsMaxExpansionHeight;
+                requestScrollerTopPaddingUpdate(false /* animate */);
+                requestPanelHeightUpdate();
+            }
+            if (mAccessibilityManager.isEnabled()) {
+                mView.setAccessibilityPaneTitle(determineAccessibilityPaneTitle());
+            }
+            mNotificationStackScroller.setMaxTopPadding(
+                    mQsMaxExpansionHeight + mQsNotificationTopPadding);
+        }
+    }
+
+    private class ZenModeControllerCallback implements ZenModeController.Callback {
+        @Override
+        public void onZenChanged(int zen) {
+            updateShowEmptyShadeView();
+        }
+    }
+
+    private class ConfigurationListener implements ConfigurationController.ConfigurationListener {
+        @Override
+        public void onDensityOrFontScaleChanged() {
+            updateShowEmptyShadeView();
+        }
+
+        @Override
+        public void onThemeChanged() {
+            final int themeResId = mView.getContext().getThemeResId();
+            if (mThemeResId == themeResId) {
+                return;
+            }
+            mThemeResId = themeResId;
+
+            reInflateViews();
+        }
+
+        @Override
+        public void onOverlayChanged() {
+            reInflateViews();
+        }
+
+        @Override
+        public void onUiModeChanged() {
+            reinflatePluginContainer();
+        }
+    }
+
+    private class StatusBarStateListener implements StateListener {
+        @Override
+        public void onStateChanged(int statusBarState) {
+            boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
+            boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
+            int oldState = mBarState;
+            boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
+            setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
+            setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
+
+            mBarState = statusBarState;
+            mKeyguardShowing = keyguardShowing;
+            if (mKeyguardShowing && isQsSplitEnabled()) {
+                mNotificationStackScroller.setVisibility(View.VISIBLE);
+                mQsFrame.setVisibility(View.VISIBLE);
+                mHomeControlsLayout.setVisibility(View.GONE);
+            }
+
+            if (oldState == StatusBarState.KEYGUARD && (goingToFullShade
+                    || statusBarState == StatusBarState.SHADE_LOCKED)) {
+                animateKeyguardStatusBarOut();
+                long
+                        delay =
+                        mBarState == StatusBarState.SHADE_LOCKED ? 0
+                                : mKeyguardStateController.calculateGoingToFullShadeDelay();
+                mQs.animateHeaderSlidingIn(delay);
+            } else if (oldState == StatusBarState.SHADE_LOCKED
+                    && statusBarState == StatusBarState.KEYGUARD) {
+                animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+                mNotificationStackScroller.resetScrollPosition();
+                // Only animate header if the header is visible. If not, it will partially
+                // animate out
+                // the top of QS
+                if (!mQsExpanded) {
+                    mQs.animateHeaderSlidingOut();
+                }
+            } else {
+                mKeyguardStatusBar.setAlpha(1f);
+                mKeyguardStatusBar.setVisibility(keyguardShowing ? View.VISIBLE : View.INVISIBLE);
+                ((PhoneStatusBarView) mBar).maybeShowDivider(keyguardShowing);
+                if (keyguardShowing && oldState != mBarState) {
+                    if (mQs != null) {
+                        mQs.hideImmediately();
+                    }
+                }
+            }
+            updateKeyguardStatusBarForHeadsUp();
+            if (keyguardShowing) {
+                updateDozingVisibilities(false /* animate */);
+            }
+            // THe update needs to happen after the headerSlide in above, otherwise the translation
+            // would reset
+            updateQSPulseExpansion();
+            maybeAnimateBottomAreaAlpha();
+            resetHorizontalPanelPosition();
+            updateQsState();
+        }
+
+        @Override
+        public void onDozeAmountChanged(float linearAmount, float amount) {
+            mInterpolatedDarkAmount = amount;
+            mLinearDarkAmount = linearAmount;
+            mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount);
+            mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount);
+            positionClockAndNotifications();
+        }
+    }
+
+    private class ExpansionCallback implements PulseExpansionHandler.ExpansionCallback {
+        public void setEmptyDragAmount(float amount) {
+            mEmptyDragAmount = amount * 0.2f;
+            positionClockAndNotifications();
+        }
+    }
+
+    private class OnAttachStateChangeListener implements View.OnAttachStateChangeListener {
+        @Override
+        public void onViewAttachedToWindow(View v) {
+            FragmentHostManager.get(mView).addTagListener(QS.TAG, mFragmentListener);
+            mStatusBarStateController.addCallback(mStatusBarStateListener);
+            mZenModeController.addCallback(mZenModeControllerCallback);
+            mConfigurationController.addCallback(mConfigurationListener);
+            mUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+            // Theme might have changed between inflating this view and attaching it to the
+            // window, so
+            // force a call to onThemeChanged
+            mConfigurationListener.onThemeChanged();
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            FragmentHostManager.get(mView).removeTagListener(QS.TAG, mFragmentListener);
+            mStatusBarStateController.removeCallback(mStatusBarStateListener);
+            mZenModeController.removeCallback(mZenModeControllerCallback);
+            mConfigurationController.removeCallback(mConfigurationListener);
+            mUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
+        }
+    }
+
+    private class OnLayoutChangeListener extends PanelViewController.OnLayoutChangeListener {
+
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+                int oldTop, int oldRight, int oldBottom) {
+            DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
+            super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
+            setIsFullWidth(mNotificationStackScroller.getWidth() == mView.getWidth());
+
+            // Update Clock Pivot
+            mKeyguardStatusView.setPivotX(mView.getWidth() / 2);
+            mKeyguardStatusView.setPivotY(
+                    (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize());
+
+            // Calculate quick setting heights.
+            int oldMaxHeight = mQsMaxExpansionHeight;
+            if (mQs != null) {
+                mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+                if (mNPVPluginManager != null) {
+                    mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
+                    mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+                }
+                mQsMaxExpansionHeight = mQs.getDesiredHeight();
+                mNotificationStackScroller.setMaxTopPadding(
+                        mQsMaxExpansionHeight + mQsNotificationTopPadding);
+            }
+            positionClockAndNotifications();
+            if (mQsExpanded && mQsFullyExpanded) {
+                mQsExpansionHeight = mQsMaxExpansionHeight;
+                requestScrollerTopPaddingUpdate(false /* animate */);
+                requestPanelHeightUpdate();
+
+                // Size has changed, start an animation.
+                if (mQsMaxExpansionHeight != oldMaxHeight) {
+                    startQsSizeChangeAnimation(oldMaxHeight, mQsMaxExpansionHeight);
+                }
+            } else if (!mQsExpanded) {
+                setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
+            }
+            updateExpandedHeight(getExpandedHeight());
+            updateHeader();
+
+            // If we are running a size change animation, the animation takes care of the height of
+            // the container. However, if we are not animating, we always need to make the QS
+            // container
+            // the desired height so when closing the QS detail, it stays smaller after the size
+            // change
+            // animation is finished but the detail view is still being animated away (this
+            // animation
+            // takes longer than the size change animation).
+            if (mQsSizeChangeAnimator == null && mQs != null) {
+                mQs.setHeightOverride(mQs.getDesiredHeight());
+            }
+            updateMaxHeadsUpTranslation();
+            updateGestureExclusionRect();
+            if (mExpandAfterLayoutRunnable != null) {
+                mExpandAfterLayoutRunnable.run();
+                mExpandAfterLayoutRunnable = null;
+            }
+            DejankUtils.stopDetectingBlockingIpcs("NVP#onLayout");
+        }
+    }
+
+    private class DebugDrawable extends Drawable {
+
+        @Override
+        public void draw(Canvas canvas) {
+            Paint p = new Paint();
+            p.setColor(Color.RED);
+            p.setStrokeWidth(2);
+            p.setStyle(Paint.Style.STROKE);
+            canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
+            p.setColor(Color.BLUE);
+            canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
+            p.setColor(Color.GREEN);
+            canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
+                    calculatePanelHeightQsExpanded(), p);
+            p.setColor(Color.YELLOW);
+            canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
+                    calculatePanelHeightShade(), p);
+            p.setColor(Color.MAGENTA);
+            canvas.drawLine(
+                    0, calculateQsTopPadding(), mView.getWidth(), calculateQsTopPadding(), p);
+            p.setColor(Color.CYAN);
+            canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
+                    mNotificationStackScroller.getTopPadding(), p);
+            p.setColor(Color.GRAY);
+            canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
+                    mClockPositionResult.clockY, p);
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+    }
+
+    private class OnConfigurationChangedListener extends
+            PanelViewController.OnConfigurationChangedListener {
+        @Override
+        public void onConfigurationChanged(Configuration newConfig) {
+            super.onConfigurationChanged(newConfig);
+            mAffordanceHelper.onConfigurationChanged();
+            if (newConfig.orientation != mLastOrientation) {
+                resetHorizontalPanelPosition();
+            }
+            mLastOrientation = newConfig.orientation;
+        }
+    }
+
+    private class OnApplyWindowInsetsListener implements View.OnApplyWindowInsetsListener {
+        public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+            mNavigationBarBottomHeight = insets.getStableInsetBottom();
+            updateMaxHeadsUpTranslation();
+            return insets;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 063d00b..8d8c8da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -43,7 +43,7 @@
     public static final int STATE_OPENING = 1;
     public static final int STATE_OPEN = 2;
 
-    PanelView mPanel;
+    PanelViewController mPanel;
     private int mState = STATE_CLOSED;
     private boolean mTracking;
 
@@ -83,7 +83,8 @@
         super.onFinishInflate();
     }
 
-    public void setPanel(PanelView pv) {
+    /** Set the PanelViewController */
+    public void setPanel(PanelViewController pv) {
         mPanel = pv;
         pv.setBar(this);
     }
@@ -96,7 +97,7 @@
         setImportantForAccessibility(important);
         updateVisibility();
 
-        if (mPanel != null) mPanel.setImportantForAccessibility(important);
+        if (mPanel != null) mPanel.getView().setImportantForAccessibility(important);
     }
 
     public float getExpansionFraction() {
@@ -108,7 +109,7 @@
     }
 
     protected void updateVisibility() {
-        mPanel.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+        mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
     }
 
     protected boolean shouldPanelBeVisible() {
@@ -131,7 +132,7 @@
         }
 
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final PanelView panel = mPanel;
+            final PanelViewController panel = mPanel;
             if (panel == null) {
                 // panel is not there, so we'll eat the gesture
                 Log.v(TAG, String.format("onTouch: no panel for touch at (%d,%d)",
@@ -149,7 +150,7 @@
                 return true;
             }
         }
-        return mPanel == null || mPanel.onTouchEvent(event);
+        return mPanel == null || mPanel.getView().dispatchTouchEvent(event);
     }
 
     public abstract void panelScrimMinFractionChanged(float minFraction);
@@ -163,7 +164,7 @@
         boolean fullyClosed = true;
         boolean fullyOpened = false;
         if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
-        PanelView pv = mPanel;
+        PanelViewController pv = mPanel;
         mExpanded = expanded;
         mPanelFraction = frac;
         updateVisibility();
@@ -192,7 +193,7 @@
 
     public void collapsePanel(boolean animate, boolean delayed, float speedUpFactor) {
         boolean waiting = false;
-        PanelView pv = mPanel;
+        PanelViewController pv = mPanel;
         if (animate && !pv.isFullyCollapsed()) {
             pv.collapse(delayed, speedUpFactor);
             waiting = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 78a5eb2..2719a32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -16,1255 +16,62 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.SystemClock;
-import android.os.VibrationEffect;
 import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewTreeObserver;
-import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.LatencyTracker;
-import com.android.systemui.DejankUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.doze.DozeLog;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.FlingAnimationUtils;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-
 public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
-    private static final int INITIAL_OPENING_PEEK_DURATION = 200;
-    private static final int PEEK_ANIMATION_DURATION = 360;
-    private static final int NO_FIXED_DURATION = -1;
-    protected long mDownTime;
-    protected boolean mTouchSlopExceededBeforeDown;
-    private float mMinExpandHeight;
-    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
-    private boolean mPanelUpdateWhenAnimatorEnds;
-    private boolean mVibrateOnOpening;
-    protected boolean mLaunchingNotification;
-    private int mFixedDuration = NO_FIXED_DURATION;
-    protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
-
-    private final void logf(String fmt, Object... args) {
-        Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
-    }
+    private PanelViewController.TouchHandler mTouchHandler;
 
     protected StatusBar mStatusBar;
     protected HeadsUpManagerPhone mHeadsUpManager;
 
-    private float mPeekHeight;
-    private float mHintDistance;
-    private float mInitialOffsetOnTouch;
-    private boolean mCollapsedAndHeadsUpOnDown;
-    private float mExpandedFraction = 0;
-    protected float mExpandedHeight = 0;
-    private boolean mPanelClosedOnDown;
-    private boolean mHasLayoutedSinceDown;
-    private float mUpdateFlingVelocity;
-    private boolean mUpdateFlingOnLayout;
-    private boolean mPeekTouching;
-    private boolean mJustPeeked;
-    private boolean mClosing;
-    protected boolean mTracking;
-    private boolean mTouchSlopExceeded;
-    private int mTrackingPointer;
     protected int mTouchSlop;
-    protected boolean mHintAnimationRunning;
-    private boolean mOverExpandedBeforeFling;
-    private boolean mTouchAboveFalsingThreshold;
-    private int mUnlockFalsingThreshold;
-    private boolean mTouchStartedInEmptyArea;
-    private boolean mMotionAborted;
-    private boolean mUpwardsWhenTresholdReached;
-    private boolean mAnimatingOnDown;
 
-    private ValueAnimator mHeightAnimator;
-    private ObjectAnimator mPeekAnimator;
-    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
-    private FlingAnimationUtils mFlingAnimationUtils;
-    private FlingAnimationUtils mFlingAnimationUtilsClosing;
-    private FlingAnimationUtils mFlingAnimationUtilsDismissing;
-    private final FalsingManager mFalsingManager;
-    private final DozeLog mDozeLog;
-    private final VibratorHelper mVibratorHelper;
-
-    /**
-     * Whether an instant expand request is currently pending and we are just waiting for layout.
-     */
-    private boolean mInstantExpanding;
-    private boolean mAnimateAfterExpanding;
-
-    PanelBar mBar;
-
-    private String mViewName;
-    private float mInitialTouchY;
-    private float mInitialTouchX;
-    private boolean mTouchDisabled;
-
-    /**
-     * Whether or not the PanelView can be expanded or collapsed with a drag.
-     */
-    private boolean mNotificationsDragEnabled;
-
-    private Interpolator mBounceInterpolator;
     protected KeyguardBottomAreaView mKeyguardBottomArea;
+    private OnConfigurationChangedListener mOnConfigurationChangedListener;
 
-    /**
-     * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
-     */
-    private float mNextCollapseSpeedUpFactor = 1.0f;
-
-    protected boolean mExpanding;
-    private boolean mGestureWaitForTouchSlop;
-    private boolean mIgnoreXTouchSlop;
-    private boolean mExpandLatencyTracking;
-    protected final KeyguardStateController mKeyguardStateController;
-    protected final SysuiStatusBarStateController mStatusBarStateController;
-
-    protected void onExpandingFinished() {
-        mBar.onExpandingFinished();
+    public PanelView(Context context) {
+        super(context);
     }
 
-    protected void onExpandingStarted() {
-    }
-
-    private void notifyExpandingStarted() {
-        if (!mExpanding) {
-            mExpanding = true;
-            onExpandingStarted();
-        }
-    }
-
-    protected final void notifyExpandingFinished() {
-        endClosing();
-        if (mExpanding) {
-            mExpanding = false;
-            onExpandingFinished();
-        }
-    }
-
-    private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) {
-        mPeekHeight = peekHeight;
-        if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
-        if (mHeightAnimator != null) {
-            return;
-        }
-        if (mPeekAnimator != null) {
-            mPeekAnimator.cancel();
-        }
-        mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight)
-                .setDuration(duration);
-        mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        mPeekAnimator.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mCancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mPeekAnimator = null;
-                if (!mCancelled && collapseWhenFinished) {
-                    postOnAnimation(mPostCollapseRunnable);
-                }
-
-            }
-        });
-        notifyExpandingStarted();
-        mPeekAnimator.start();
-        mJustPeeked = true;
-    }
-
-    public PanelView(Context context, AttributeSet attrs, FalsingManager falsingManager,
-            DozeLog dozeLog, KeyguardStateController keyguardStateController,
-            SysuiStatusBarStateController statusBarStateController) {
+    public PanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mKeyguardStateController = keyguardStateController;
-        mStatusBarStateController = statusBarStateController;
-        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
-        mFlingAnimationUtils = new FlingAnimationUtils(displayMetrics,
-                0.6f /* maxLengthSeconds */, 0.6f /* speedUpFactor */);
-        mFlingAnimationUtilsClosing = new FlingAnimationUtils(displayMetrics,
-                0.5f /* maxLengthSeconds */, 0.6f /* speedUpFactor */);
-        mFlingAnimationUtilsDismissing = new FlingAnimationUtils(displayMetrics,
-                0.5f /* maxLengthSeconds */, 0.2f /* speedUpFactor */, 0.6f /* x2 */,
-                0.84f /* y2 */);
-        mBounceInterpolator = new BounceInterpolator();
-        mFalsingManager = falsingManager;
-        mDozeLog = dozeLog;
-        mNotificationsDragEnabled =
-                getResources().getBoolean(R.bool.config_enableNotificationShadeDrag);
-        mVibratorHelper = Dependency.get(VibratorHelper.class);
-        mVibrateOnOpening = mContext.getResources().getBoolean(
-                R.bool.config_vibrateOnIconAnimation);
     }
 
-    protected void loadDimens() {
-        final Resources res = getContext().getResources();
-        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        mTouchSlop = configuration.getScaledTouchSlop();
-        mHintDistance = res.getDimension(R.dimen.hint_move_distance);
-        mUnlockFalsingThreshold = res.getDimensionPixelSize(R.dimen.unlock_falsing_threshold);
+    public PanelView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
     }
 
-    private void addMovement(MotionEvent event) {
-        // Add movement to velocity tracker using raw screen X and Y coordinates instead
-        // of window coordinates because the window frame may be moving at the same time.
-        float deltaX = event.getRawX() - event.getX();
-        float deltaY = event.getRawY() - event.getY();
-        event.offsetLocation(deltaX, deltaY);
-        mVelocityTracker.addMovement(event);
-        event.offsetLocation(-deltaX, -deltaY);
+    public PanelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
     }
 
-    public void setTouchAndAnimationDisabled(boolean disabled) {
-        mTouchDisabled = disabled;
-        if (mTouchDisabled) {
-            cancelHeightAnimator();
-            if (mTracking) {
-                onTrackingStopped(true /* expanded */);
-            }
-            notifyExpandingFinished();
-        }
+    public void setOnTouchListener(PanelViewController.TouchHandler touchHandler) {
+        super.setOnTouchListener(touchHandler);
+        mTouchHandler = touchHandler;
     }
 
-    public void startExpandLatencyTracking() {
-        if (LatencyTracker.isEnabled(mContext)) {
-            LatencyTracker.getInstance(mContext).onActionStart(
-                    LatencyTracker.ACTION_EXPAND_PANEL);
-            mExpandLatencyTracking = true;
-        }
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (mInstantExpanding
-                || (mTouchDisabled && event.getActionMasked() != MotionEvent.ACTION_CANCEL)
-                || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
-            return false;
-        }
-
-        // If dragging should not expand the notifications shade, then return false.
-        if (!mNotificationsDragEnabled) {
-            if (mTracking) {
-                // Turn off tracking if it's on or the shade can get stuck in the down position.
-                onTrackingStopped(true /* expand */);
-            }
-            return false;
-        }
-
-        // On expanding, single mouse click expands the panel instead of dragging.
-        if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
-            if (event.getAction() == MotionEvent.ACTION_UP) {
-                expand(true);
-            }
-            return true;
-        }
-
-        /*
-         * We capture touch events here and update the expand height here in case according to
-         * the users fingers. This also handles multi-touch.
-         *
-         * If the user just clicks shortly, we show a quick peek of the shade.
-         *
-         * Flinging is also enabled in order to open or close the shade.
-         */
-
-        int pointerIndex = event.findPointerIndex(mTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mTrackingPointer = event.getPointerId(pointerIndex);
-        }
-        final float x = event.getX(pointerIndex);
-        final float y = event.getY(pointerIndex);
-
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
-            mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
-        }
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
-                mJustPeeked = false;
-                mMinExpandHeight = 0.0f;
-                mPanelClosedOnDown = isFullyCollapsed();
-                mHasLayoutedSinceDown = false;
-                mUpdateFlingOnLayout = false;
-                mMotionAborted = false;
-                mPeekTouching = mPanelClosedOnDown;
-                mDownTime = SystemClock.uptimeMillis();
-                mTouchAboveFalsingThreshold = false;
-                mCollapsedAndHeadsUpOnDown = isFullyCollapsed()
-                        && mHeadsUpManager.hasPinnedHeadsUp();
-                addMovement(event);
-                if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning)
-                        || mPeekAnimator != null) {
-                    mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning)
-                            || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
-                    cancelHeightAnimator();
-                    cancelPeek();
-                    onTrackingStarted();
-                }
-                if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
-                        && !mStatusBar.isBouncerShowing()) {
-                    startOpening(event);
-                }
-                break;
-
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    final float newY = event.getY(newIndex);
-                    final float newX = event.getX(newIndex);
-                    mTrackingPointer = event.getPointerId(newIndex);
-                    startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN:
-                if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
-                    mMotionAborted = true;
-                    endMotionEvent(event, x, y, true /* forceCancel */);
-                    return false;
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                addMovement(event);
-                float h = y - mInitialTouchY;
-
-                // If the panel was collapsed when touching, we only need to check for the
-                // y-component of the gesture, as we have no conflicting horizontal gesture.
-                if (Math.abs(h) > mTouchSlop
-                        && (Math.abs(h) > Math.abs(x - mInitialTouchX)
-                        || mIgnoreXTouchSlop)) {
-                    mTouchSlopExceeded = true;
-                    if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
-                        if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
-                            startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
-                            h = 0;
-                        }
-                        cancelHeightAnimator();
-                        onTrackingStarted();
-                    }
-                }
-                float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
-                if (newHeight > mPeekHeight) {
-                    if (mPeekAnimator != null) {
-                        mPeekAnimator.cancel();
-                    }
-                    mJustPeeked = false;
-                } else if (mPeekAnimator == null && mJustPeeked) {
-                    // The initial peek has finished, but we haven't dragged as far yet, lets
-                    // speed it up by starting at the peek height.
-                    mInitialOffsetOnTouch = mExpandedHeight;
-                    mInitialTouchY = y;
-                    mMinExpandHeight = mExpandedHeight;
-                    mJustPeeked = false;
-                }
-                newHeight = Math.max(newHeight, mMinExpandHeight);
-                if (-h >= getFalsingThreshold()) {
-                    mTouchAboveFalsingThreshold = true;
-                    mUpwardsWhenTresholdReached = isDirectionUpwards(x, y);
-                }
-                if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking) &&
-                        !isTrackingBlocked()) {
-                    setExpandedHeightInternal(newHeight);
-                }
-                break;
-
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                addMovement(event);
-                endMotionEvent(event, x, y, false /* forceCancel */);
-                break;
-        }
-        return !mGestureWaitForTouchSlop || mTracking;
-    }
-
-    private void startOpening(MotionEvent event) {
-        runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
-                false /* collapseWhenFinished */);
-        notifyBarPanelExpansionChanged();
-        maybeVibrateOnOpening();
-
-        //TODO: keyguard opens QS a different way; log that too?
-
-        // Log the position of the swipe that opened the panel
-        float width = mStatusBar.getDisplayWidth();
-        float height = mStatusBar.getDisplayHeight();
-        int rot = mStatusBar.getRotation();
-
-        mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
-                (int) (event.getX() / width * 100),
-                (int) (event.getY() / height * 100),
-                rot);
-    }
-
-    protected void maybeVibrateOnOpening() {
-        if (mVibrateOnOpening) {
-            mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
-        }
-    }
-
-    protected abstract float getOpeningHeight();
-
-    /**
-     * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
-     * horizontal direction
-     */
-    private boolean isDirectionUpwards(float x, float y) {
-        float xDiff = x - mInitialTouchX;
-        float yDiff = y - mInitialTouchY;
-        if (yDiff >= 0) {
-            return false;
-        }
-        return Math.abs(yDiff) >= Math.abs(xDiff);
-    }
-
-    protected void startExpandingFromPeek() {
-        mStatusBar.handlePeekToExpandTransistion();
-    }
-
-    protected void startExpandMotion(float newX, float newY, boolean startTracking,
-            float expandedHeight) {
-        mInitialOffsetOnTouch = expandedHeight;
-        mInitialTouchY = newY;
-        mInitialTouchX = newX;
-        if (startTracking) {
-            mTouchSlopExceeded = true;
-            setExpandedHeight(mInitialOffsetOnTouch);
-            onTrackingStarted();
-        }
-    }
-
-    private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
-        mTrackingPointer = -1;
-        if ((mTracking && mTouchSlopExceeded)
-                || Math.abs(x - mInitialTouchX) > mTouchSlop
-                || Math.abs(y - mInitialTouchY) > mTouchSlop
-                || event.getActionMasked() == MotionEvent.ACTION_CANCEL
-                || forceCancel) {
-            mVelocityTracker.computeCurrentVelocity(1000);
-            float vel = mVelocityTracker.getYVelocity();
-            float vectorVel = (float) Math.hypot(
-                    mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
-
-            boolean expand = flingExpands(vel, vectorVel, x, y)
-                    || event.getActionMasked() == MotionEvent.ACTION_CANCEL
-                    || forceCancel;
-            mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
-                    mStatusBar.isFalsingThresholdNeeded(),
-                    mStatusBar.isWakeUpComingFromTouch());
-                    // Log collapse gesture if on lock screen.
-                    if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
-                        float displayDensity = mStatusBar.getDisplayDensity();
-                        int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
-                        int velocityDp = (int) Math.abs(vel / displayDensity);
-                        mLockscreenGestureLogger.write(
-                                MetricsEvent.ACTION_LS_UNLOCK,
-                                heightDp, velocityDp);
-                    }
-            fling(vel, expand, isFalseTouch(x, y));
-            onTrackingStopped(expand);
-            mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
-            if (mUpdateFlingOnLayout) {
-                mUpdateFlingVelocity = vel;
-            }
-        } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
-                && !mStatusBar.isBouncerShowing()
-                && !mKeyguardStateController.isKeyguardFadingAway()) {
-            long timePassed = SystemClock.uptimeMillis() - mDownTime;
-            if (timePassed < ViewConfiguration.getLongPressTimeout()) {
-                // Lets show the user that he can actually expand the panel
-                runPeekAnimation(PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
-            } else {
-                // We need to collapse the panel since we peeked to the small height.
-                postOnAnimation(mPostCollapseRunnable);
-            }
-        } else if (!mStatusBar.isBouncerShowing()) {
-            boolean expands = onEmptySpaceClick(mInitialTouchX);
-            onTrackingStopped(expands);
-        }
-
-        mVelocityTracker.clear();
-        mPeekTouching = false;
-    }
-
-    protected float getCurrentExpandVelocity() {
-        mVelocityTracker.computeCurrentVelocity(1000);
-        return mVelocityTracker.getYVelocity();
-    }
-
-    private int getFalsingThreshold() {
-        float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
-        return (int) (mUnlockFalsingThreshold * factor);
-    }
-
-    protected abstract boolean shouldGestureWaitForTouchSlop();
-
-    protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
-
-    protected void onTrackingStopped(boolean expand) {
-        mTracking = false;
-        mBar.onTrackingStopped(expand);
-        notifyBarPanelExpansionChanged();
-    }
-
-    protected void onTrackingStarted() {
-        endClosing();
-        mTracking = true;
-        mBar.onTrackingStarted();
-        notifyExpandingStarted();
-        notifyBarPanelExpansionChanged();
+    public void setOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
+        mOnConfigurationChangedListener = listener;
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled
-                || (mMotionAborted && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
-            return false;
-        }
-
-        /*
-         * If the user drags anywhere inside the panel we intercept it if the movement is
-         * upwards. This allows closing the shade from anywhere inside the panel.
-         *
-         * We only do this if the current content is scrolled to the bottom,
-         * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling gesture
-         * possible.
-         */
-        int pointerIndex = event.findPointerIndex(mTrackingPointer);
-        if (pointerIndex < 0) {
-            pointerIndex = 0;
-            mTrackingPointer = event.getPointerId(pointerIndex);
-        }
-        final float x = event.getX(pointerIndex);
-        final float y = event.getY(pointerIndex);
-        boolean scrolledToBottom = isScrolledToBottom();
-
-        switch (event.getActionMasked()) {
-            case MotionEvent.ACTION_DOWN:
-                mStatusBar.userActivity();
-                mAnimatingOnDown = mHeightAnimator != null;
-                mMinExpandHeight = 0.0f;
-                mDownTime = SystemClock.uptimeMillis();
-                if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
-                        || mPeekAnimator != null) {
-                    cancelHeightAnimator();
-                    cancelPeek();
-                    mTouchSlopExceeded = true;
-                    return true;
-                }
-                mInitialTouchY = y;
-                mInitialTouchX = x;
-                mTouchStartedInEmptyArea = !isInContentBounds(x, y);
-                mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
-                mJustPeeked = false;
-                mMotionAborted = false;
-                mPanelClosedOnDown = isFullyCollapsed();
-                mCollapsedAndHeadsUpOnDown = false;
-                mHasLayoutedSinceDown = false;
-                mUpdateFlingOnLayout = false;
-                mTouchAboveFalsingThreshold = false;
-                addMovement(event);
-                break;
-            case MotionEvent.ACTION_POINTER_UP:
-                final int upPointer = event.getPointerId(event.getActionIndex());
-                if (mTrackingPointer == upPointer) {
-                    // gesture is ongoing, find a new pointer to track
-                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                    mTrackingPointer = event.getPointerId(newIndex);
-                    mInitialTouchX = event.getX(newIndex);
-                    mInitialTouchY = event.getY(newIndex);
-                }
-                break;
-            case MotionEvent.ACTION_POINTER_DOWN:
-                if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
-                    mMotionAborted = true;
-                    mVelocityTracker.clear();
-                }
-                break;
-            case MotionEvent.ACTION_MOVE:
-                final float h = y - mInitialTouchY;
-                addMovement(event);
-                if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
-                    float hAbs = Math.abs(h);
-                    if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
-                            && hAbs > Math.abs(x - mInitialTouchX)) {
-                        cancelHeightAnimator();
-                        startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
-                        return true;
-                    }
-                }
-                break;
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                mVelocityTracker.clear();
-                break;
-        }
-        return false;
-    }
-
-    /**
-     * @return Whether a pair of coordinates are inside the visible view content bounds.
-     */
-    protected abstract boolean isInContentBounds(float x, float y);
-
-    protected void cancelHeightAnimator() {
-        if (mHeightAnimator != null) {
-            if (mHeightAnimator.isRunning()) {
-                mPanelUpdateWhenAnimatorEnds = false;
-            }
-            mHeightAnimator.cancel();
-        }
-        endClosing();
-    }
-
-    private void endClosing() {
-        if (mClosing) {
-            mClosing = false;
-            onClosingFinished();
-        }
-    }
-
-    protected boolean isScrolledToBottom() {
-        return true;
-    }
-
-    protected float getContentHeight() {
-        return mExpandedHeight;
+        return mTouchHandler.onInterceptTouchEvent(event);
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        loadDimens();
+    public void dispatchConfigurationChanged(Configuration newConfig) {
+        super.dispatchConfigurationChanged(newConfig);
+        mOnConfigurationChangedListener.onConfigurationChanged(newConfig);
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-        loadDimens();
-    }
-
-    /**
-     * @param vel the current vertical velocity of the motion
-     * @param vectorVel the length of the vectorial velocity
-     * @return whether a fling should expands the panel; contracts otherwise
-     */
-    protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
-        if (mFalsingManager.isUnlockingDisabled()) {
-            return true;
-        }
-
-        if (isFalseTouch(x, y)) {
-            return true;
-        }
-        if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-            return shouldExpandWhenNotFlinging();
-        } else {
-            return vel > 0;
-        }
-    }
-
-    protected boolean shouldExpandWhenNotFlinging() {
-        return getExpandedFraction() > 0.5f;
-    }
-
-    /**
-     * @param x the final x-coordinate when the finger was lifted
-     * @param y the final y-coordinate when the finger was lifted
-     * @return whether this motion should be regarded as a false touch
-     */
-    private boolean isFalseTouch(float x, float y) {
-        if (!mStatusBar.isFalsingThresholdNeeded()) {
-            return false;
-        }
-        if (mFalsingManager.isClassiferEnabled()) {
-            return mFalsingManager.isFalseTouch();
-        }
-        if (!mTouchAboveFalsingThreshold) {
-            return true;
-        }
-        if (mUpwardsWhenTresholdReached) {
-            return false;
-        }
-        return !isDirectionUpwards(x, y);
-    }
-
-    protected void fling(float vel, boolean expand) {
-        fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
-    }
-
-    protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
-        fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
-    }
-
-    protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
-            boolean expandBecauseOfFalsing) {
-        cancelPeek();
-        float target = expand ? getMaxPanelHeight() : 0;
-        if (!expand) {
-            mClosing = true;
-        }
-        flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
-    }
-
-    protected void flingToHeight(float vel, boolean expand, float target,
-            float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
-        // Hack to make the expand transition look nice when clear all button is visible - we make
-        // the animation only to the last notification, and then jump to the maximum panel height so
-        // clear all just fades in and the decelerating motion is towards the last notification.
-        final boolean clearAllExpandHack = expand && fullyExpandedClearAllVisible()
-                && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
-                && !isClearAllVisible();
-        if (clearAllExpandHack) {
-            target = getMaxPanelHeight() - getClearAllHeight();
-        }
-        if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
-            notifyExpandingFinished();
-            return;
-        }
-        mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
-        ValueAnimator animator = createHeightAnimator(target);
-        if (expand) {
-            if (expandBecauseOfFalsing && vel < 0) {
-                vel = 0;
-            }
-            mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight());
-            if (vel == 0) {
-                animator.setDuration(350);
-            }
-        } else {
-            if (shouldUseDismissingAnimation()) {
-                if (vel == 0) {
-                    animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
-                    long duration = (long) (200 + mExpandedHeight / getHeight() * 100);
-                    animator.setDuration(duration);
-                } else {
-                    mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
-                            getHeight());
-                }
-            } else {
-                mFlingAnimationUtilsClosing
-                        .apply(animator, mExpandedHeight, target, vel, getHeight());
-            }
-
-            // Make it shorter if we run a canned animation
-            if (vel == 0) {
-                animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
-            }
-            if (mFixedDuration != NO_FIXED_DURATION) {
-                animator.setDuration(mFixedDuration);
-            }
-        }
-        animator.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mCancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (clearAllExpandHack && !mCancelled) {
-                    setExpandedHeightInternal(getMaxPanelHeight());
-                }
-                setAnimator(null);
-                if (!mCancelled) {
-                    notifyExpandingFinished();
-                }
-                notifyBarPanelExpansionChanged();
-            }
-        });
-        setAnimator(animator);
-        animator.start();
-    }
-
-    protected abstract boolean shouldUseDismissingAnimation();
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mViewName = getResources().getResourceName(getId());
-    }
-
-    public String getName() {
-        return mViewName;
-    }
-
-    public void setExpandedHeight(float height) {
-        if (DEBUG) logf("setExpandedHeight(%.1f)", height);
-        setExpandedHeightInternal(height + getOverExpansionPixels());
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mStatusBar.onPanelLaidOut();
-        requestPanelHeightUpdate();
-        mHasLayoutedSinceDown = true;
-        if (mUpdateFlingOnLayout) {
-            abortAnimations();
-            fling(mUpdateFlingVelocity, true /* expands */);
-            mUpdateFlingOnLayout = false;
-        }
-    }
-
-    protected void requestPanelHeightUpdate() {
-        float currentMaxPanelHeight = getMaxPanelHeight();
-
-        if (isFullyCollapsed()) {
-            return;
-        }
-
-        if (currentMaxPanelHeight == mExpandedHeight) {
-            return;
-        }
-
-        if (mPeekAnimator != null || mPeekTouching) {
-            return;
-        }
-
-        if (mTracking && !isTrackingBlocked()) {
-            return;
-        }
-
-        if (mHeightAnimator != null) {
-            mPanelUpdateWhenAnimatorEnds = true;
-            return;
-        }
-
-        setExpandedHeight(currentMaxPanelHeight);
-    }
-
-    public void setExpandedHeightInternal(float h) {
-        if (mExpandLatencyTracking && h != 0f) {
-            DejankUtils.postAfterTraversal(() -> LatencyTracker.getInstance(mContext).onActionEnd(
-                    LatencyTracker.ACTION_EXPAND_PANEL));
-            mExpandLatencyTracking = false;
-        }
-        float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
-        if (mHeightAnimator == null) {
-            float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
-            if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
-                setOverExpansion(overExpansionPixels, true /* isPixels */);
-            }
-            mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
-        } else {
-            mExpandedHeight = h;
-            if (mOverExpandedBeforeFling) {
-                setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
-            }
-        }
-
-        // If we are closing the panel and we are almost there due to a slow decelerating
-        // interpolator, abort the animation.
-        if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
-            mExpandedHeight = 0f;
-            if (mHeightAnimator != null) {
-                mHeightAnimator.end();
-            }
-        }
-        mExpandedFraction = Math.min(1f,
-                fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
-        onHeightUpdated(mExpandedHeight);
-        notifyBarPanelExpansionChanged();
-    }
-
-    /**
-     * @return true if the panel tracking should be temporarily blocked; this is used when a
-     *         conflicting gesture (opening QS) is happening
-     */
-    protected abstract boolean isTrackingBlocked();
-
-    protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
-
-    protected abstract void onHeightUpdated(float expandedHeight);
-
-    protected abstract float getOverExpansionAmount();
-
-    protected abstract float getOverExpansionPixels();
-
-    /**
-     * This returns the maximum height of the panel. Children should override this if their
-     * desired height is not the full height.
-     *
-     * @return the default implementation simply returns the maximum height.
-     */
-    protected abstract int getMaxPanelHeight();
-
-    public void setExpandedFraction(float frac) {
-        setExpandedHeight(getMaxPanelHeight() * frac);
-    }
-
-    public float getExpandedHeight() {
-        return mExpandedHeight;
-    }
-
-    public float getExpandedFraction() {
-        return mExpandedFraction;
-    }
-
-    public boolean isFullyExpanded() {
-        return mExpandedHeight >= getMaxPanelHeight();
-    }
-
-    public boolean isFullyCollapsed() {
-        return mExpandedFraction <= 0.0f;
-    }
-
-    public boolean isCollapsing() {
-        return mClosing || mLaunchingNotification;
-    }
-
-    public boolean isTracking() {
-        return mTracking;
-    }
-
-    public void setBar(PanelBar panelBar) {
-        mBar = panelBar;
-    }
-
-    public void collapse(boolean delayed, float speedUpFactor) {
-        if (DEBUG) logf("collapse: " + this);
-        if (canPanelBeCollapsed()) {
-            cancelHeightAnimator();
-            notifyExpandingStarted();
-
-            // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
-            mClosing = true;
-            if (delayed) {
-                mNextCollapseSpeedUpFactor = speedUpFactor;
-                postDelayed(mFlingCollapseRunnable, 120);
-            } else {
-                fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
-            }
-        }
-    }
-
-    public boolean canPanelBeCollapsed() {
-        return !isFullyCollapsed() && !mTracking && !mClosing;
-    }
-
-    private final Runnable mFlingCollapseRunnable = new Runnable() {
-        @Override
-        public void run() {
-            fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
-                    false /* expandBecauseOfFalsing */);
-        }
-    };
-
-    public void cancelPeek() {
-        boolean cancelled = false;
-        if (mPeekAnimator != null) {
-            cancelled = true;
-            mPeekAnimator.cancel();
-        }
-
-        if (cancelled) {
-            // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
-            // notify mBar that we might have closed ourselves.
-            notifyBarPanelExpansionChanged();
-        }
-    }
-
-    public void expand(final boolean animate) {
-        if (!isFullyCollapsed() && !isCollapsing()) {
-            return;
-        }
-
-        mInstantExpanding = true;
-        mAnimateAfterExpanding = animate;
-        mUpdateFlingOnLayout = false;
-        abortAnimations();
-        cancelPeek();
-        if (mTracking) {
-            onTrackingStopped(true /* expands */); // The panel is expanded after this call.
-        }
-        if (mExpanding) {
-            notifyExpandingFinished();
-        }
-        notifyBarPanelExpansionChanged();
-
-        // Wait for window manager to pickup the change, so we know the maximum height of the panel
-        // then.
-        getViewTreeObserver().addOnGlobalLayoutListener(
-                new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        if (!mInstantExpanding) {
-                            getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                            return;
-                        }
-                        if (mStatusBar.getStatusBarWindow().getHeight()
-                                != mStatusBar.getStatusBarHeight()) {
-                            getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                            if (mAnimateAfterExpanding) {
-                                notifyExpandingStarted();
-                                fling(0, true /* expand */);
-                            } else {
-                                setExpandedFraction(1f);
-                            }
-                            mInstantExpanding = false;
-                        }
-                    }
-                });
-
-        // Make sure a layout really happens.
-        requestLayout();
-    }
-
-    public void instantCollapse() {
-        abortAnimations();
-        setExpandedFraction(0f);
-        if (mExpanding) {
-            notifyExpandingFinished();
-        }
-        if (mInstantExpanding) {
-            mInstantExpanding = false;
-            notifyBarPanelExpansionChanged();
-        }
-    }
-
-    private void abortAnimations() {
-        cancelPeek();
-        cancelHeightAnimator();
-        removeCallbacks(mPostCollapseRunnable);
-        removeCallbacks(mFlingCollapseRunnable);
-    }
-
-    protected void onClosingFinished() {
-        mBar.onClosingFinished();
-    }
-
-
-    protected void startUnlockHintAnimation() {
-
-        // We don't need to hint the user if an animation is already running or the user is changing
-        // the expansion.
-        if (mHeightAnimator != null || mTracking) {
-            return;
-        }
-        cancelPeek();
-        notifyExpandingStarted();
-        startUnlockHintAnimationPhase1(() -> {
-            notifyExpandingFinished();
-            onUnlockHintFinished();
-            mHintAnimationRunning = false;
-        });
-        onUnlockHintStarted();
-        mHintAnimationRunning = true;
-    }
-
-    protected void onUnlockHintFinished() {
-        mStatusBar.onHintFinished();
-    }
-
-    protected void onUnlockHintStarted() {
-        mStatusBar.onUnlockHintStarted();
-    }
-
-    public boolean isUnlockHintRunning() {
-        return mHintAnimationRunning;
-    }
-
-    /**
-     * Phase 1: Move everything upwards.
-     */
-    private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
-        float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
-        ValueAnimator animator = createHeightAnimator(target);
-        animator.setDuration(250);
-        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
-        animator.addListener(new AnimatorListenerAdapter() {
-            private boolean mCancelled;
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                mCancelled = true;
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                if (mCancelled) {
-                    setAnimator(null);
-                    onAnimationFinished.run();
-                } else {
-                    startUnlockHintAnimationPhase2(onAnimationFinished);
-                }
-            }
-        });
-        animator.start();
-        setAnimator(animator);
-
-        View[] viewsToAnimate = {
-                mKeyguardBottomArea.getIndicationArea(),
-                mStatusBar.getAmbientIndicationContainer()};
-        for (View v : viewsToAnimate) {
-            if (v == null) {
-                continue;
-            }
-            v.animate()
-                    .translationY(-mHintDistance)
-                    .setDuration(250)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .withEndAction(() -> v.animate()
-                            .translationY(0)
-                            .setDuration(450)
-                            .setInterpolator(mBounceInterpolator)
-                            .start())
-                    .start();
-        }
-    }
-
-    private void setAnimator(ValueAnimator animator) {
-        mHeightAnimator = animator;
-        if (animator == null && mPanelUpdateWhenAnimatorEnds) {
-            mPanelUpdateWhenAnimatorEnds = false;
-            requestPanelHeightUpdate();
-        }
-    }
-
-    /**
-     * Phase 2: Bounce down.
-     */
-    private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
-        ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
-        animator.setDuration(450);
-        animator.setInterpolator(mBounceInterpolator);
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                setAnimator(null);
-                onAnimationFinished.run();
-                notifyBarPanelExpansionChanged();
-            }
-        });
-        animator.start();
-        setAnimator(animator);
-    }
-
-    private ValueAnimator createHeightAnimator(float targetHeight) {
-        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
-        animator.addUpdateListener(
-                animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
-        return animator;
-    }
-
-    protected void notifyBarPanelExpansionChanged() {
-        if (mBar != null) {
-            mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
-                    || mPeekAnimator != null || mInstantExpanding
-                    || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null);
-        }
-        for (int i = 0; i < mExpansionListeners.size(); i++) {
-            mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
-        }
-    }
-
-    public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
-        mExpansionListeners.add(panelExpansionListener);
-    }
-
-    protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
-
-    /**
-     * Gets called when the user performs a click anywhere in the empty area of the panel.
-     *
-     * @return whether the panel will be expanded after the action performed by this method
-     */
-    protected boolean onEmptySpaceClick(float x) {
-        if (mHintAnimationRunning) {
-            return true;
-        }
-        return onMiddleClicked();
-    }
-
-    protected final Runnable mPostCollapseRunnable = new Runnable() {
-        @Override
-        public void run() {
-            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-        }
-    };
-
-    protected abstract boolean onMiddleClicked();
-
-    protected abstract boolean isDozing();
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
-                + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s touchDisabled=%s"
-                + "]",
-                this.getClass().getSimpleName(),
-                getExpandedHeight(),
-                getMaxPanelHeight(),
-                mClosing?"T":"f",
-                mTracking?"T":"f",
-                mJustPeeked?"T":"f",
-                mPeekAnimator, ((mPeekAnimator!=null && mPeekAnimator.isStarted())?" (started)":""),
-                mHeightAnimator, ((mHeightAnimator !=null && mHeightAnimator.isStarted())?" (started)":""),
-                mTouchDisabled?"T":"f"
-        ));
-    }
-
-    public abstract void resetViews(boolean animate);
-
-    protected abstract float getPeekHeight();
-    /**
-     * @return whether "Clear all" button will be visible when the panel is fully expanded
-     */
-    protected abstract boolean fullyExpandedClearAllVisible();
-
-    protected abstract boolean isClearAllVisible();
-
-    /**
-     * @return the height of the clear all button, in pixels
-     */
-    protected abstract int getClearAllHeight();
-
-    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
-        mHeadsUpManager = headsUpManager;
-    }
-
-    public void setLaunchingNotification(boolean launchingNotification) {
-        mLaunchingNotification = launchingNotification;
-    }
-
-    public void collapseWithDuration(int animationDuration) {
-        mFixedDuration = animationDuration;
-        collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-        mFixedDuration = NO_FIXED_DURATION;
+    interface OnConfigurationChangedListener {
+        void onConfigurationChanged(Configuration newConfig);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
new file mode 100644
index 0000000..3d8e09a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -0,0 +1,1297 @@
+/*
+ * 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.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.util.Log;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.animation.Interpolator;
+
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.LatencyTracker;
+import com.android.systemui.DejankUtils;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.doze.DozeLog;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public abstract class PanelViewController {
+    public static final boolean DEBUG = PanelBar.DEBUG;
+    public static final String TAG = PanelView.class.getSimpleName();
+    private static final int INITIAL_OPENING_PEEK_DURATION = 200;
+    private static final int PEEK_ANIMATION_DURATION = 360;
+    private static final int NO_FIXED_DURATION = -1;
+    protected long mDownTime;
+    protected boolean mTouchSlopExceededBeforeDown;
+    private float mMinExpandHeight;
+    private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
+    private boolean mPanelUpdateWhenAnimatorEnds;
+    private boolean mVibrateOnOpening;
+    protected boolean mLaunchingNotification;
+    private int mFixedDuration = NO_FIXED_DURATION;
+    protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>();
+
+    private void logf(String fmt, Object... args) {
+        Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
+    }
+
+    protected StatusBar mStatusBar;
+    protected HeadsUpManagerPhone mHeadsUpManager;
+
+    private float mPeekHeight;
+    private float mHintDistance;
+    private float mInitialOffsetOnTouch;
+    private boolean mCollapsedAndHeadsUpOnDown;
+    private float mExpandedFraction = 0;
+    protected float mExpandedHeight = 0;
+    private boolean mPanelClosedOnDown;
+    private boolean mHasLayoutedSinceDown;
+    private float mUpdateFlingVelocity;
+    private boolean mUpdateFlingOnLayout;
+    private boolean mPeekTouching;
+    private boolean mJustPeeked;
+    private boolean mClosing;
+    protected boolean mTracking;
+    private boolean mTouchSlopExceeded;
+    private int mTrackingPointer;
+    protected int mTouchSlop;
+    protected boolean mHintAnimationRunning;
+    private boolean mOverExpandedBeforeFling;
+    private boolean mTouchAboveFalsingThreshold;
+    private int mUnlockFalsingThreshold;
+    private boolean mTouchStartedInEmptyArea;
+    private boolean mMotionAborted;
+    private boolean mUpwardsWhenThresholdReached;
+    private boolean mAnimatingOnDown;
+
+    private ValueAnimator mHeightAnimator;
+    private ObjectAnimator mPeekAnimator;
+    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
+    private FlingAnimationUtils mFlingAnimationUtils;
+    private FlingAnimationUtils mFlingAnimationUtilsClosing;
+    private FlingAnimationUtils mFlingAnimationUtilsDismissing;
+    private final LatencyTracker mLatencyTracker;
+    private final FalsingManager mFalsingManager;
+    private final DozeLog mDozeLog;
+    private final VibratorHelper mVibratorHelper;
+
+    /**
+     * Whether an instant expand request is currently pending and we are just waiting for layout.
+     */
+    private boolean mInstantExpanding;
+    private boolean mAnimateAfterExpanding;
+
+    PanelBar mBar;
+
+    private String mViewName;
+    private float mInitialTouchY;
+    private float mInitialTouchX;
+    private boolean mTouchDisabled;
+
+    /**
+     * Whether or not the PanelView can be expanded or collapsed with a drag.
+     */
+    private boolean mNotificationsDragEnabled;
+
+    private Interpolator mBounceInterpolator;
+    protected KeyguardBottomAreaView mKeyguardBottomArea;
+
+    /**
+     * Speed-up factor to be used when {@link #mFlingCollapseRunnable} runs the next time.
+     */
+    private float mNextCollapseSpeedUpFactor = 1.0f;
+
+    protected boolean mExpanding;
+    private boolean mGestureWaitForTouchSlop;
+    private boolean mIgnoreXTouchSlop;
+    private boolean mExpandLatencyTracking;
+    private final PanelView mView;
+    protected final Resources mResources;
+    protected final KeyguardStateController mKeyguardStateController;
+    protected final SysuiStatusBarStateController mStatusBarStateController;
+
+    protected void onExpandingFinished() {
+        mBar.onExpandingFinished();
+    }
+
+    protected void onExpandingStarted() {
+    }
+
+    private void notifyExpandingStarted() {
+        if (!mExpanding) {
+            mExpanding = true;
+            onExpandingStarted();
+        }
+    }
+
+    protected final void notifyExpandingFinished() {
+        endClosing();
+        if (mExpanding) {
+            mExpanding = false;
+            onExpandingFinished();
+        }
+    }
+
+    private void runPeekAnimation(long duration, float peekHeight, boolean collapseWhenFinished) {
+        mPeekHeight = peekHeight;
+        if (DEBUG) logf("peek to height=%.1f", mPeekHeight);
+        if (mHeightAnimator != null) {
+            return;
+        }
+        if (mPeekAnimator != null) {
+            mPeekAnimator.cancel();
+        }
+        mPeekAnimator = ObjectAnimator.ofFloat(this, "expandedHeight", mPeekHeight).setDuration(
+                duration);
+        mPeekAnimator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        mPeekAnimator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCancelled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mPeekAnimator = null;
+                if (!mCancelled && collapseWhenFinished) {
+                    mView.postOnAnimation(mPostCollapseRunnable);
+                }
+
+            }
+        });
+        notifyExpandingStarted();
+        mPeekAnimator.start();
+        mJustPeeked = true;
+    }
+
+    public PanelViewController(PanelView view,
+            FalsingManager falsingManager, DozeLog dozeLog,
+            KeyguardStateController keyguardStateController,
+            SysuiStatusBarStateController statusBarStateController, VibratorHelper vibratorHelper,
+            LatencyTracker latencyTracker, FlingAnimationUtils.Builder flingAnimationUtilsBuilder) {
+        mView = view;
+        mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                mViewName = mResources.getResourceName(mView.getId());
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+            }
+        });
+
+        mView.addOnLayoutChangeListener(createLayoutChangeListener());
+        mView.setOnTouchListener(createTouchHandler());
+        mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener());
+
+        mResources = mView.getResources();
+        mKeyguardStateController = keyguardStateController;
+        mStatusBarStateController = statusBarStateController;
+        mFlingAnimationUtils = flingAnimationUtilsBuilder
+                .reset()
+                .setMaxLengthSeconds(0.6f)
+                .setSpeedUpFactor(0.6f)
+                .build();
+        mFlingAnimationUtilsClosing = flingAnimationUtilsBuilder
+                .reset()
+                .setMaxLengthSeconds(0.5f)
+                .setSpeedUpFactor(0.6f)
+                .build();
+        mFlingAnimationUtilsDismissing = flingAnimationUtilsBuilder
+                .reset()
+                .setMaxLengthSeconds(0.5f)
+                .setSpeedUpFactor(0.6f)
+                .setX2(0.6f)
+                .setY2(0.84f)
+                .build();
+        mLatencyTracker = latencyTracker;
+        mBounceInterpolator = new BounceInterpolator();
+        mFalsingManager = falsingManager;
+        mDozeLog = dozeLog;
+        mNotificationsDragEnabled = mResources.getBoolean(
+                R.bool.config_enableNotificationShadeDrag);
+        mVibratorHelper = vibratorHelper;
+        mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
+    }
+
+    protected void loadDimens() {
+        final ViewConfiguration configuration = ViewConfiguration.get(mView.getContext());
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mHintDistance = mResources.getDimension(R.dimen.hint_move_distance);
+        mUnlockFalsingThreshold = mResources.getDimensionPixelSize(
+                R.dimen.unlock_falsing_threshold);
+    }
+
+    private void addMovement(MotionEvent event) {
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        float deltaX = event.getRawX() - event.getX();
+        float deltaY = event.getRawY() - event.getY();
+        event.offsetLocation(deltaX, deltaY);
+        mVelocityTracker.addMovement(event);
+        event.offsetLocation(-deltaX, -deltaY);
+    }
+
+    public void setTouchAndAnimationDisabled(boolean disabled) {
+        mTouchDisabled = disabled;
+        if (mTouchDisabled) {
+            cancelHeightAnimator();
+            if (mTracking) {
+                onTrackingStopped(true /* expanded */);
+            }
+            notifyExpandingFinished();
+        }
+    }
+
+    public void startExpandLatencyTracking() {
+        if (mLatencyTracker.isEnabled()) {
+            mLatencyTracker.onActionStart(LatencyTracker.ACTION_EXPAND_PANEL);
+            mExpandLatencyTracking = true;
+        }
+    }
+
+    private void startOpening(MotionEvent event) {
+        runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(),
+                false /* collapseWhenFinished */);
+        notifyBarPanelExpansionChanged();
+        maybeVibrateOnOpening();
+
+        //TODO: keyguard opens QS a different way; log that too?
+
+        // Log the position of the swipe that opened the panel
+        float width = mStatusBar.getDisplayWidth();
+        float height = mStatusBar.getDisplayHeight();
+        int rot = mStatusBar.getRotation();
+
+        mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
+                (int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
+    }
+
+    protected void maybeVibrateOnOpening() {
+        if (mVibrateOnOpening) {
+            mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+        }
+    }
+
+    protected abstract float getOpeningHeight();
+
+    /**
+     * @return whether the swiping direction is upwards and above a 45 degree angle compared to the
+     * horizontal direction
+     */
+    private boolean isDirectionUpwards(float x, float y) {
+        float xDiff = x - mInitialTouchX;
+        float yDiff = y - mInitialTouchY;
+        if (yDiff >= 0) {
+            return false;
+        }
+        return Math.abs(yDiff) >= Math.abs(xDiff);
+    }
+
+    protected void startExpandingFromPeek() {
+        mStatusBar.handlePeekToExpandTransistion();
+    }
+
+    protected void startExpandMotion(float newX, float newY, boolean startTracking,
+            float expandedHeight) {
+        mInitialOffsetOnTouch = expandedHeight;
+        mInitialTouchY = newY;
+        mInitialTouchX = newX;
+        if (startTracking) {
+            mTouchSlopExceeded = true;
+            setExpandedHeight(mInitialOffsetOnTouch);
+            onTrackingStarted();
+        }
+    }
+
+    private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
+        mTrackingPointer = -1;
+        if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialTouchX) > mTouchSlop
+                || Math.abs(y - mInitialTouchY) > mTouchSlop
+                || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+            mVelocityTracker.computeCurrentVelocity(1000);
+            float vel = mVelocityTracker.getYVelocity();
+            float vectorVel = (float) Math.hypot(
+                    mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+
+            boolean expand = flingExpands(vel, vectorVel, x, y)
+                    || event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel;
+            mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
+                    mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch());
+            // Log collapse gesture if on lock screen.
+            if (!expand && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+                float displayDensity = mStatusBar.getDisplayDensity();
+                int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
+                int velocityDp = (int) Math.abs(vel / displayDensity);
+                mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
+            }
+            fling(vel, expand, isFalseTouch(x, y));
+            onTrackingStopped(expand);
+            mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
+            if (mUpdateFlingOnLayout) {
+                mUpdateFlingVelocity = vel;
+            }
+        } else if (mPanelClosedOnDown && !mHeadsUpManager.hasPinnedHeadsUp() && !mTracking
+                && !mStatusBar.isBouncerShowing()
+                && !mKeyguardStateController.isKeyguardFadingAway()) {
+            long timePassed = SystemClock.uptimeMillis() - mDownTime;
+            if (timePassed < ViewConfiguration.getLongPressTimeout()) {
+                // Lets show the user that he can actually expand the panel
+                runPeekAnimation(
+                        PEEK_ANIMATION_DURATION, getPeekHeight(), true /* collapseWhenFinished */);
+            } else {
+                // We need to collapse the panel since we peeked to the small height.
+                mView.postOnAnimation(mPostCollapseRunnable);
+            }
+        } else if (!mStatusBar.isBouncerShowing()) {
+            boolean expands = onEmptySpaceClick(mInitialTouchX);
+            onTrackingStopped(expands);
+        }
+
+        mVelocityTracker.clear();
+        mPeekTouching = false;
+    }
+
+    protected float getCurrentExpandVelocity() {
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getYVelocity();
+    }
+
+    private int getFalsingThreshold() {
+        float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+        return (int) (mUnlockFalsingThreshold * factor);
+    }
+
+    protected abstract boolean shouldGestureWaitForTouchSlop();
+
+    protected abstract boolean shouldGestureIgnoreXTouchSlop(float x, float y);
+
+    protected void onTrackingStopped(boolean expand) {
+        mTracking = false;
+        mBar.onTrackingStopped(expand);
+        notifyBarPanelExpansionChanged();
+    }
+
+    protected void onTrackingStarted() {
+        endClosing();
+        mTracking = true;
+        mBar.onTrackingStarted();
+        notifyExpandingStarted();
+        notifyBarPanelExpansionChanged();
+    }
+
+    /**
+     * @return Whether a pair of coordinates are inside the visible view content bounds.
+     */
+    protected abstract boolean isInContentBounds(float x, float y);
+
+    protected void cancelHeightAnimator() {
+        if (mHeightAnimator != null) {
+            if (mHeightAnimator.isRunning()) {
+                mPanelUpdateWhenAnimatorEnds = false;
+            }
+            mHeightAnimator.cancel();
+        }
+        endClosing();
+    }
+
+    private void endClosing() {
+        if (mClosing) {
+            mClosing = false;
+            onClosingFinished();
+        }
+    }
+
+    protected boolean isScrolledToBottom() {
+        return true;
+    }
+
+    protected float getContentHeight() {
+        return mExpandedHeight;
+    }
+
+    /**
+     * @param vel       the current vertical velocity of the motion
+     * @param vectorVel the length of the vectorial velocity
+     * @return whether a fling should expands the panel; contracts otherwise
+     */
+    protected boolean flingExpands(float vel, float vectorVel, float x, float y) {
+        if (mFalsingManager.isUnlockingDisabled()) {
+            return true;
+        }
+
+        if (isFalseTouch(x, y)) {
+            return true;
+        }
+        if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
+            return shouldExpandWhenNotFlinging();
+        } else {
+            return vel > 0;
+        }
+    }
+
+    protected boolean shouldExpandWhenNotFlinging() {
+        return getExpandedFraction() > 0.5f;
+    }
+
+    /**
+     * @param x the final x-coordinate when the finger was lifted
+     * @param y the final y-coordinate when the finger was lifted
+     * @return whether this motion should be regarded as a false touch
+     */
+    private boolean isFalseTouch(float x, float y) {
+        if (!mStatusBar.isFalsingThresholdNeeded()) {
+            return false;
+        }
+        if (mFalsingManager.isClassifierEnabled()) {
+            return mFalsingManager.isFalseTouch();
+        }
+        if (!mTouchAboveFalsingThreshold) {
+            return true;
+        }
+        if (mUpwardsWhenThresholdReached) {
+            return false;
+        }
+        return !isDirectionUpwards(x, y);
+    }
+
+    protected void fling(float vel, boolean expand) {
+        fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, false);
+    }
+
+    protected void fling(float vel, boolean expand, boolean expandBecauseOfFalsing) {
+        fling(vel, expand, 1.0f /* collapseSpeedUpFactor */, expandBecauseOfFalsing);
+    }
+
+    protected void fling(float vel, boolean expand, float collapseSpeedUpFactor,
+            boolean expandBecauseOfFalsing) {
+        cancelPeek();
+        float target = expand ? getMaxPanelHeight() : 0;
+        if (!expand) {
+            mClosing = true;
+        }
+        flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
+    }
+
+    protected void flingToHeight(float vel, boolean expand, float target,
+            float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
+        // Hack to make the expand transition look nice when clear all button is visible - we make
+        // the animation only to the last notification, and then jump to the maximum panel height so
+        // clear all just fades in and the decelerating motion is towards the last notification.
+        final boolean
+                clearAllExpandHack =
+                expand && fullyExpandedClearAllVisible()
+                        && mExpandedHeight < getMaxPanelHeight() - getClearAllHeight()
+                        && !isClearAllVisible();
+        if (clearAllExpandHack) {
+            target = getMaxPanelHeight() - getClearAllHeight();
+        }
+        if (target == mExpandedHeight || getOverExpansionAmount() > 0f && expand) {
+            notifyExpandingFinished();
+            return;
+        }
+        mOverExpandedBeforeFling = getOverExpansionAmount() > 0f;
+        ValueAnimator animator = createHeightAnimator(target);
+        if (expand) {
+            if (expandBecauseOfFalsing && vel < 0) {
+                vel = 0;
+            }
+            mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, mView.getHeight());
+            if (vel == 0) {
+                animator.setDuration(350);
+            }
+        } else {
+            if (shouldUseDismissingAnimation()) {
+                if (vel == 0) {
+                    animator.setInterpolator(Interpolators.PANEL_CLOSE_ACCELERATED);
+                    long duration = (long) (200 + mExpandedHeight / mView.getHeight() * 100);
+                    animator.setDuration(duration);
+                } else {
+                    mFlingAnimationUtilsDismissing.apply(animator, mExpandedHeight, target, vel,
+                            mView.getHeight());
+                }
+            } else {
+                mFlingAnimationUtilsClosing.apply(
+                        animator, mExpandedHeight, target, vel, mView.getHeight());
+            }
+
+            // Make it shorter if we run a canned animation
+            if (vel == 0) {
+                animator.setDuration((long) (animator.getDuration() / collapseSpeedUpFactor));
+            }
+            if (mFixedDuration != NO_FIXED_DURATION) {
+                animator.setDuration(mFixedDuration);
+            }
+        }
+        animator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCancelled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (clearAllExpandHack && !mCancelled) {
+                    setExpandedHeightInternal(getMaxPanelHeight());
+                }
+                setAnimator(null);
+                if (!mCancelled) {
+                    notifyExpandingFinished();
+                }
+                notifyBarPanelExpansionChanged();
+            }
+        });
+        setAnimator(animator);
+        animator.start();
+    }
+
+    protected abstract boolean shouldUseDismissingAnimation();
+
+    public String getName() {
+        return mViewName;
+    }
+
+    public void setExpandedHeight(float height) {
+        if (DEBUG) logf("setExpandedHeight(%.1f)", height);
+        setExpandedHeightInternal(height + getOverExpansionPixels());
+    }
+
+    protected void requestPanelHeightUpdate() {
+        float currentMaxPanelHeight = getMaxPanelHeight();
+
+        if (isFullyCollapsed()) {
+            return;
+        }
+
+        if (currentMaxPanelHeight == mExpandedHeight) {
+            return;
+        }
+
+        if (mPeekAnimator != null || mPeekTouching) {
+            return;
+        }
+
+        if (mTracking && !isTrackingBlocked()) {
+            return;
+        }
+
+        if (mHeightAnimator != null) {
+            mPanelUpdateWhenAnimatorEnds = true;
+            return;
+        }
+
+        setExpandedHeight(currentMaxPanelHeight);
+    }
+
+    public void setExpandedHeightInternal(float h) {
+        if (mExpandLatencyTracking && h != 0f) {
+            DejankUtils.postAfterTraversal(
+                    () -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
+            mExpandLatencyTracking = false;
+        }
+        float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
+        if (mHeightAnimator == null) {
+            float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
+            if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
+                setOverExpansion(overExpansionPixels, true /* isPixels */);
+            }
+            mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
+        } else {
+            mExpandedHeight = h;
+            if (mOverExpandedBeforeFling) {
+                setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
+            }
+        }
+
+        // If we are closing the panel and we are almost there due to a slow decelerating
+        // interpolator, abort the animation.
+        if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
+            mExpandedHeight = 0f;
+            if (mHeightAnimator != null) {
+                mHeightAnimator.end();
+            }
+        }
+        mExpandedFraction = Math.min(1f,
+                fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
+        onHeightUpdated(mExpandedHeight);
+        notifyBarPanelExpansionChanged();
+    }
+
+    /**
+     * @return true if the panel tracking should be temporarily blocked; this is used when a
+     * conflicting gesture (opening QS) is happening
+     */
+    protected abstract boolean isTrackingBlocked();
+
+    protected abstract void setOverExpansion(float overExpansion, boolean isPixels);
+
+    protected abstract void onHeightUpdated(float expandedHeight);
+
+    protected abstract float getOverExpansionAmount();
+
+    protected abstract float getOverExpansionPixels();
+
+    /**
+     * This returns the maximum height of the panel. Children should override this if their
+     * desired height is not the full height.
+     *
+     * @return the default implementation simply returns the maximum height.
+     */
+    protected abstract int getMaxPanelHeight();
+
+    public void setExpandedFraction(float frac) {
+        setExpandedHeight(getMaxPanelHeight() * frac);
+    }
+
+    public float getExpandedHeight() {
+        return mExpandedHeight;
+    }
+
+    public float getExpandedFraction() {
+        return mExpandedFraction;
+    }
+
+    public boolean isFullyExpanded() {
+        return mExpandedHeight >= getMaxPanelHeight();
+    }
+
+    public boolean isFullyCollapsed() {
+        return mExpandedFraction <= 0.0f;
+    }
+
+    public boolean isCollapsing() {
+        return mClosing || mLaunchingNotification;
+    }
+
+    public boolean isTracking() {
+        return mTracking;
+    }
+
+    public void setBar(PanelBar panelBar) {
+        mBar = panelBar;
+    }
+
+    public void collapse(boolean delayed, float speedUpFactor) {
+        if (DEBUG) logf("collapse: " + this);
+        if (canPanelBeCollapsed()) {
+            cancelHeightAnimator();
+            notifyExpandingStarted();
+
+            // Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
+            mClosing = true;
+            if (delayed) {
+                mNextCollapseSpeedUpFactor = speedUpFactor;
+                mView.postDelayed(mFlingCollapseRunnable, 120);
+            } else {
+                fling(0, false /* expand */, speedUpFactor, false /* expandBecauseOfFalsing */);
+            }
+        }
+    }
+
+    public boolean canPanelBeCollapsed() {
+        return !isFullyCollapsed() && !mTracking && !mClosing;
+    }
+
+    private final Runnable mFlingCollapseRunnable = new Runnable() {
+        @Override
+        public void run() {
+            fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
+                    false /* expandBecauseOfFalsing */);
+        }
+    };
+
+    public void cancelPeek() {
+        boolean cancelled = false;
+        if (mPeekAnimator != null) {
+            cancelled = true;
+            mPeekAnimator.cancel();
+        }
+
+        if (cancelled) {
+            // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
+            // notify mBar that we might have closed ourselves.
+            notifyBarPanelExpansionChanged();
+        }
+    }
+
+    public void expand(final boolean animate) {
+        if (!isFullyCollapsed() && !isCollapsing()) {
+            return;
+        }
+
+        mInstantExpanding = true;
+        mAnimateAfterExpanding = animate;
+        mUpdateFlingOnLayout = false;
+        abortAnimations();
+        cancelPeek();
+        if (mTracking) {
+            onTrackingStopped(true /* expands */); // The panel is expanded after this call.
+        }
+        if (mExpanding) {
+            notifyExpandingFinished();
+        }
+        notifyBarPanelExpansionChanged();
+
+        // Wait for window manager to pickup the change, so we know the maximum height of the panel
+        // then.
+        mView.getViewTreeObserver().addOnGlobalLayoutListener(
+                new ViewTreeObserver.OnGlobalLayoutListener() {
+                    @Override
+                    public void onGlobalLayout() {
+                        if (!mInstantExpanding) {
+                            mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                            return;
+                        }
+                        if (mStatusBar.getStatusBarWindow().getHeight()
+                                != mStatusBar.getStatusBarHeight()) {
+                            mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                            if (mAnimateAfterExpanding) {
+                                notifyExpandingStarted();
+                                fling(0, true /* expand */);
+                            } else {
+                                setExpandedFraction(1f);
+                            }
+                            mInstantExpanding = false;
+                        }
+                    }
+                });
+
+        // Make sure a layout really happens.
+        mView.requestLayout();
+    }
+
+    public void instantCollapse() {
+        abortAnimations();
+        setExpandedFraction(0f);
+        if (mExpanding) {
+            notifyExpandingFinished();
+        }
+        if (mInstantExpanding) {
+            mInstantExpanding = false;
+            notifyBarPanelExpansionChanged();
+        }
+    }
+
+    private void abortAnimations() {
+        cancelPeek();
+        cancelHeightAnimator();
+        mView.removeCallbacks(mPostCollapseRunnable);
+        mView.removeCallbacks(mFlingCollapseRunnable);
+    }
+
+    protected void onClosingFinished() {
+        mBar.onClosingFinished();
+    }
+
+
+    protected void startUnlockHintAnimation() {
+
+        // We don't need to hint the user if an animation is already running or the user is changing
+        // the expansion.
+        if (mHeightAnimator != null || mTracking) {
+            return;
+        }
+        cancelPeek();
+        notifyExpandingStarted();
+        startUnlockHintAnimationPhase1(() -> {
+            notifyExpandingFinished();
+            onUnlockHintFinished();
+            mHintAnimationRunning = false;
+        });
+        onUnlockHintStarted();
+        mHintAnimationRunning = true;
+    }
+
+    protected void onUnlockHintFinished() {
+        mStatusBar.onHintFinished();
+    }
+
+    protected void onUnlockHintStarted() {
+        mStatusBar.onUnlockHintStarted();
+    }
+
+    public boolean isUnlockHintRunning() {
+        return mHintAnimationRunning;
+    }
+
+    /**
+     * Phase 1: Move everything upwards.
+     */
+    private void startUnlockHintAnimationPhase1(final Runnable onAnimationFinished) {
+        float target = Math.max(0, getMaxPanelHeight() - mHintDistance);
+        ValueAnimator animator = createHeightAnimator(target);
+        animator.setDuration(250);
+        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        animator.addListener(new AnimatorListenerAdapter() {
+            private boolean mCancelled;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mCancelled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mCancelled) {
+                    setAnimator(null);
+                    onAnimationFinished.run();
+                } else {
+                    startUnlockHintAnimationPhase2(onAnimationFinished);
+                }
+            }
+        });
+        animator.start();
+        setAnimator(animator);
+
+        View[] viewsToAnimate = {
+                mKeyguardBottomArea.getIndicationArea(),
+                mStatusBar.getAmbientIndicationContainer()};
+        for (View v : viewsToAnimate) {
+            if (v == null) {
+                continue;
+            }
+            v.animate().translationY(-mHintDistance).setDuration(250).setInterpolator(
+                    Interpolators.FAST_OUT_SLOW_IN).withEndAction(() -> v.animate().translationY(
+                    0).setDuration(450).setInterpolator(mBounceInterpolator).start()).start();
+        }
+    }
+
+    private void setAnimator(ValueAnimator animator) {
+        mHeightAnimator = animator;
+        if (animator == null && mPanelUpdateWhenAnimatorEnds) {
+            mPanelUpdateWhenAnimatorEnds = false;
+            requestPanelHeightUpdate();
+        }
+    }
+
+    /**
+     * Phase 2: Bounce down.
+     */
+    private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
+        ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
+        animator.setDuration(450);
+        animator.setInterpolator(mBounceInterpolator);
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                setAnimator(null);
+                onAnimationFinished.run();
+                notifyBarPanelExpansionChanged();
+            }
+        });
+        animator.start();
+        setAnimator(animator);
+    }
+
+    private ValueAnimator createHeightAnimator(float targetHeight) {
+        ValueAnimator animator = ValueAnimator.ofFloat(mExpandedHeight, targetHeight);
+        animator.addUpdateListener(
+                animation -> setExpandedHeightInternal((float) animation.getAnimatedValue()));
+        return animator;
+    }
+
+    protected void notifyBarPanelExpansionChanged() {
+        if (mBar != null) {
+            mBar.panelExpansionChanged(
+                    mExpandedFraction,
+                    mExpandedFraction > 0f || mPeekAnimator != null || mInstantExpanding
+                            || isPanelVisibleBecauseOfHeadsUp() || mTracking
+                            || mHeightAnimator != null);
+        }
+        for (int i = 0; i < mExpansionListeners.size(); i++) {
+            mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
+        }
+    }
+
+    public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
+        mExpansionListeners.add(panelExpansionListener);
+    }
+
+    protected abstract boolean isPanelVisibleBecauseOfHeadsUp();
+
+    /**
+     * Gets called when the user performs a click anywhere in the empty area of the panel.
+     *
+     * @return whether the panel will be expanded after the action performed by this method
+     */
+    protected boolean onEmptySpaceClick(float x) {
+        if (mHintAnimationRunning) {
+            return true;
+        }
+        return onMiddleClicked();
+    }
+
+    protected final Runnable mPostCollapseRunnable = new Runnable() {
+        @Override
+        public void run() {
+            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+        }
+    };
+
+    protected abstract boolean onMiddleClicked();
+
+    protected abstract boolean isDozing();
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(String.format("[PanelView(%s): expandedHeight=%f maxPanelHeight=%d closing=%s"
+                        + " tracking=%s justPeeked=%s peekAnim=%s%s timeAnim=%s%s "
+                        + "touchDisabled=%s" + "]",
+                this.getClass().getSimpleName(), getExpandedHeight(), getMaxPanelHeight(),
+                mClosing ? "T" : "f", mTracking ? "T" : "f", mJustPeeked ? "T" : "f", mPeekAnimator,
+                ((mPeekAnimator != null && mPeekAnimator.isStarted()) ? " (started)" : ""),
+                mHeightAnimator,
+                ((mHeightAnimator != null && mHeightAnimator.isStarted()) ? " (started)" : ""),
+                mTouchDisabled ? "T" : "f"));
+    }
+
+    public abstract void resetViews(boolean animate);
+
+    protected abstract float getPeekHeight();
+
+    /**
+     * @return whether "Clear all" button will be visible when the panel is fully expanded
+     */
+    protected abstract boolean fullyExpandedClearAllVisible();
+
+    protected abstract boolean isClearAllVisible();
+
+    /**
+     * @return the height of the clear all button, in pixels
+     */
+    protected abstract int getClearAllHeight();
+
+    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
+    public void setLaunchingNotification(boolean launchingNotification) {
+        mLaunchingNotification = launchingNotification;
+    }
+
+    public void collapseWithDuration(int animationDuration) {
+        mFixedDuration = animationDuration;
+        collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+        mFixedDuration = NO_FIXED_DURATION;
+    }
+
+    public ViewGroup getView() {
+        // TODO: remove this method, or at least reduce references to it.
+        return mView;
+    }
+
+    public boolean isEnabled() {
+        return mView.isEnabled();
+    }
+
+    public OnLayoutChangeListener createLayoutChangeListener() {
+        return new OnLayoutChangeListener();
+    }
+
+    protected TouchHandler createTouchHandler() {
+        return new TouchHandler();
+    }
+
+    protected OnConfigurationChangedListener createOnConfigurationChangedListener() {
+        return new OnConfigurationChangedListener();
+    }
+
+    public class TouchHandler implements View.OnTouchListener {
+        public boolean onInterceptTouchEvent(MotionEvent event) {
+            if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
+                    && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
+                return false;
+            }
+
+            /*
+             * If the user drags anywhere inside the panel we intercept it if the movement is
+             * upwards. This allows closing the shade from anywhere inside the panel.
+             *
+             * We only do this if the current content is scrolled to the bottom,
+             * i.e isScrolledToBottom() is true and therefore there is no conflicting scrolling
+             * gesture
+             * possible.
+             */
+            int pointerIndex = event.findPointerIndex(mTrackingPointer);
+            if (pointerIndex < 0) {
+                pointerIndex = 0;
+                mTrackingPointer = event.getPointerId(pointerIndex);
+            }
+            final float x = event.getX(pointerIndex);
+            final float y = event.getY(pointerIndex);
+            boolean scrolledToBottom = isScrolledToBottom();
+
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    mStatusBar.userActivity();
+                    mAnimatingOnDown = mHeightAnimator != null;
+                    mMinExpandHeight = 0.0f;
+                    mDownTime = SystemClock.uptimeMillis();
+                    if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
+                            || mPeekAnimator != null) {
+                        cancelHeightAnimator();
+                        cancelPeek();
+                        mTouchSlopExceeded = true;
+                        return true;
+                    }
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mTouchStartedInEmptyArea = !isInContentBounds(x, y);
+                    mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
+                    mJustPeeked = false;
+                    mMotionAborted = false;
+                    mPanelClosedOnDown = isFullyCollapsed();
+                    mCollapsedAndHeadsUpOnDown = false;
+                    mHasLayoutedSinceDown = false;
+                    mUpdateFlingOnLayout = false;
+                    mTouchAboveFalsingThreshold = false;
+                    addMovement(event);
+                    break;
+                case MotionEvent.ACTION_POINTER_UP:
+                    final int upPointer = event.getPointerId(event.getActionIndex());
+                    if (mTrackingPointer == upPointer) {
+                        // gesture is ongoing, find a new pointer to track
+                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                        mTrackingPointer = event.getPointerId(newIndex);
+                        mInitialTouchX = event.getX(newIndex);
+                        mInitialTouchY = event.getY(newIndex);
+                    }
+                    break;
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+                        mMotionAborted = true;
+                        mVelocityTracker.clear();
+                    }
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    final float h = y - mInitialTouchY;
+                    addMovement(event);
+                    if (scrolledToBottom || mTouchStartedInEmptyArea || mAnimatingOnDown) {
+                        float hAbs = Math.abs(h);
+                        if ((h < -mTouchSlop || (mAnimatingOnDown && hAbs > mTouchSlop))
+                                && hAbs > Math.abs(x - mInitialTouchX)) {
+                            cancelHeightAnimator();
+                            startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
+                            return true;
+                        }
+                    }
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                case MotionEvent.ACTION_UP:
+                    mVelocityTracker.clear();
+                    break;
+            }
+            return false;
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            if (mInstantExpanding || (mTouchDisabled
+                    && event.getActionMasked() != MotionEvent.ACTION_CANCEL) || (mMotionAborted
+                    && event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
+                return false;
+            }
+
+            // If dragging should not expand the notifications shade, then return false.
+            if (!mNotificationsDragEnabled) {
+                if (mTracking) {
+                    // Turn off tracking if it's on or the shade can get stuck in the down position.
+                    onTrackingStopped(true /* expand */);
+                }
+                return false;
+            }
+
+            // On expanding, single mouse click expands the panel instead of dragging.
+            if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
+                if (event.getAction() == MotionEvent.ACTION_UP) {
+                    expand(true);
+                }
+                return true;
+            }
+
+            /*
+             * We capture touch events here and update the expand height here in case according to
+             * the users fingers. This also handles multi-touch.
+             *
+             * If the user just clicks shortly, we show a quick peek of the shade.
+             *
+             * Flinging is also enabled in order to open or close the shade.
+             */
+
+            int pointerIndex = event.findPointerIndex(mTrackingPointer);
+            if (pointerIndex < 0) {
+                pointerIndex = 0;
+                mTrackingPointer = event.getPointerId(pointerIndex);
+            }
+            final float x = event.getX(pointerIndex);
+            final float y = event.getY(pointerIndex);
+
+            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
+                mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
+            }
+
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
+                    mJustPeeked = false;
+                    mMinExpandHeight = 0.0f;
+                    mPanelClosedOnDown = isFullyCollapsed();
+                    mHasLayoutedSinceDown = false;
+                    mUpdateFlingOnLayout = false;
+                    mMotionAborted = false;
+                    mPeekTouching = mPanelClosedOnDown;
+                    mDownTime = SystemClock.uptimeMillis();
+                    mTouchAboveFalsingThreshold = false;
+                    mCollapsedAndHeadsUpOnDown =
+                            isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
+                    addMovement(event);
+                    if (!mGestureWaitForTouchSlop || (mHeightAnimator != null
+                            && !mHintAnimationRunning) || mPeekAnimator != null) {
+                        mTouchSlopExceeded =
+                                (mHeightAnimator != null && !mHintAnimationRunning)
+                                        || mPeekAnimator != null || mTouchSlopExceededBeforeDown;
+                        cancelHeightAnimator();
+                        cancelPeek();
+                        onTrackingStarted();
+                    }
+                    if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
+                            && !mStatusBar.isBouncerShowing()) {
+                        startOpening(event);
+                    }
+                    break;
+
+                case MotionEvent.ACTION_POINTER_UP:
+                    final int upPointer = event.getPointerId(event.getActionIndex());
+                    if (mTrackingPointer == upPointer) {
+                        // gesture is ongoing, find a new pointer to track
+                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                        final float newY = event.getY(newIndex);
+                        final float newX = event.getX(newIndex);
+                        mTrackingPointer = event.getPointerId(newIndex);
+                        startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
+                    }
+                    break;
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
+                        mMotionAborted = true;
+                        endMotionEvent(event, x, y, true /* forceCancel */);
+                        return false;
+                    }
+                    break;
+                case MotionEvent.ACTION_MOVE:
+                    addMovement(event);
+                    float h = y - mInitialTouchY;
+
+                    // If the panel was collapsed when touching, we only need to check for the
+                    // y-component of the gesture, as we have no conflicting horizontal gesture.
+                    if (Math.abs(h) > mTouchSlop && (Math.abs(h) > Math.abs(x - mInitialTouchX)
+                            || mIgnoreXTouchSlop)) {
+                        mTouchSlopExceeded = true;
+                        if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
+                            if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
+                                startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
+                                h = 0;
+                            }
+                            cancelHeightAnimator();
+                            onTrackingStarted();
+                        }
+                    }
+                    float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
+                    if (newHeight > mPeekHeight) {
+                        if (mPeekAnimator != null) {
+                            mPeekAnimator.cancel();
+                        }
+                        mJustPeeked = false;
+                    } else if (mPeekAnimator == null && mJustPeeked) {
+                        // The initial peek has finished, but we haven't dragged as far yet, lets
+                        // speed it up by starting at the peek height.
+                        mInitialOffsetOnTouch = mExpandedHeight;
+                        mInitialTouchY = y;
+                        mMinExpandHeight = mExpandedHeight;
+                        mJustPeeked = false;
+                    }
+                    newHeight = Math.max(newHeight, mMinExpandHeight);
+                    if (-h >= getFalsingThreshold()) {
+                        mTouchAboveFalsingThreshold = true;
+                        mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
+                    }
+                    if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking)
+                            && !isTrackingBlocked()) {
+                        setExpandedHeightInternal(newHeight);
+                    }
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    addMovement(event);
+                    endMotionEvent(event, x, y, false /* forceCancel */);
+                    break;
+            }
+            return !mGestureWaitForTouchSlop || mTracking;
+        }
+    }
+
+    public class OnLayoutChangeListener implements View.OnLayoutChangeListener {
+        @Override
+        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+                int oldTop, int oldRight, int oldBottom) {
+            mStatusBar.onPanelLaidOut();
+            requestPanelHeightUpdate();
+            mHasLayoutedSinceDown = true;
+            if (mUpdateFlingOnLayout) {
+                abortAnimations();
+                fling(mUpdateFlingVelocity, true /* expands */);
+                mUpdateFlingOnLayout = false;
+            }
+        }
+    }
+
+    public class OnConfigurationChangedListener implements
+            PanelView.OnConfigurationChangedListener {
+        @Override
+        public void onConfigurationChanged(Configuration newConfig) {
+            loadDimens();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 312ca26..45f3bf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -236,7 +236,7 @@
     public void onPanelFullyOpened() {
         super.onPanelFullyOpened();
         if (!mIsFullyOpenedPanel) {
-            mPanel.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+            mPanel.getView().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
         }
         mIsFullyOpenedPanel = true;
         maybeShowDivider(!mBar.mPanelExpanded);
@@ -420,7 +420,8 @@
 
     void maybeShowDivider(boolean showDivider) {
         int state =
-                showDivider && NotificationPanelView.isQsSplitEnabled() ? View.VISIBLE : View.GONE;
+                showDivider && NotificationPanelViewController.isQsSplitEnabled()
+                        ? View.VISIBLE : View.GONE;
         mDividerContainer.setVisibility(state);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 57e7014..866dc2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -79,7 +79,7 @@
     public void instantExpandNotificationsPanel() {
         // Make our window larger and the panel expanded.
         getStatusBar().makeExpandedVisible(true /* force */);
-        getNotificationPanelView().expand(false /* animate */);
+        getNotificationPanelViewController().expand(false /* animate */);
         mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
     }
 
@@ -123,8 +123,9 @@
 
         // TODO(b/62444020): remove when this bug is fixed
         Log.v(TAG, "mStatusBarWindow: " + getStatusBarWindowView() + " canPanelBeCollapsed(): "
-                + getNotificationPanelView().canPanelBeCollapsed());
-        if (getStatusBarWindowView() != null && getNotificationPanelView().canPanelBeCollapsed()) {
+                + getNotificationPanelViewController().canPanelBeCollapsed());
+        if (getStatusBarWindowView() != null
+                && getNotificationPanelViewController().canPanelBeCollapsed()) {
             // release focus immediately to kick off focus change transition
             mStatusBarWindowController.setStatusBarFocusable(false);
 
@@ -138,7 +139,7 @@
 
     @Override
     public boolean closeShadeIfOpen() {
-        if (!getNotificationPanelView().isFullyCollapsed()) {
+        if (!getNotificationPanelViewController().isFullyCollapsed()) {
             mCommandQueue.animateCollapsePanels(
                     CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
             getStatusBar().visibilityChanged(false);
@@ -149,15 +150,14 @@
 
     @Override
     public void postOnShadeExpanded(Runnable executable) {
-        getNotificationPanelView().getViewTreeObserver().addOnGlobalLayoutListener(
+        getNotificationPanelViewController().addOnGlobalLayoutListener(
                 new ViewTreeObserver.OnGlobalLayoutListener() {
                     @Override
                     public void onGlobalLayout() {
                         if (getStatusBar().getStatusBarWindow().getHeight()
                                 != getStatusBar().getStatusBarHeight()) {
-                            getNotificationPanelView().getViewTreeObserver()
-                                    .removeOnGlobalLayoutListener(this);
-                            getNotificationPanelView().post(executable);
+                            getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
+                            getNotificationPanelViewController().getView().post(executable);
                         }
                     }
                 });
@@ -187,7 +187,7 @@
 
     @Override
     public boolean collapsePanel() {
-        if (!getNotificationPanelView().isFullyCollapsed()) {
+        if (!getNotificationPanelViewController().isFullyCollapsed()) {
             // close the shade if it was open
             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                     true /* force */, true /* delayed */);
@@ -230,7 +230,7 @@
         return (PhoneStatusBarView) getStatusBar().getStatusBarView();
     }
 
-    private NotificationPanelView getNotificationPanelView() {
-        return getStatusBar().getPanel();
+    private NotificationPanelViewController getNotificationPanelViewController() {
+        return getStatusBar().getPanelController();
     }
 }
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 a6a734a..277a761 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -131,7 +131,6 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.EventLogTags;
 import com.android.systemui.InitController;
-import com.android.systemui.Interpolators;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
@@ -199,7 +198,6 @@
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.NotificationListController;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
@@ -208,6 +206,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -392,7 +391,8 @@
     private final DismissCallbackRegistry mDismissCallbackRegistry;
 
     // expanded notifications
-    protected NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
+    // the sliding/resizing panel within the notification window
+    protected NotificationPanelViewController mNotificationPanelViewController;
 
     // settings
     private QSPanel mQSPanel;
@@ -461,8 +461,8 @@
                 mUserSetup = userSetup;
                 if (!mUserSetup && mStatusBarView != null)
                     animateCollapseQuickSettings();
-                if (mNotificationPanel != null) {
-                    mNotificationPanel.setUserSetupComplete(mUserSetup);
+                if (mNotificationPanelViewController != null) {
+                    mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
                 }
                 updateQsExpansionEnabled();
             }
@@ -913,9 +913,8 @@
 
         mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
         mDozeServiceHost.initialize(this, mNotificationIconAreaController,
-                mStatusBarKeyguardViewManager,
-                mStatusBarWindowViewController,
-                mNotificationPanel, mAmbientIndicationContainer);
+                mStatusBarKeyguardViewManager, mStatusBarWindowViewController,
+                mNotificationPanelViewController, mAmbientIndicationContainer);
 
         mConfigurationController.addCallback(this);
 
@@ -985,8 +984,6 @@
 
         // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
-        mNotificationPanel = mSuperStatusBarViewFactory.getNotificationPanelView();
-
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
         NotificationListContainer notifListContainer = (NotificationListContainer) mStackScroller;
         mNotificationLogger.setUpWithContainer(notifListContainer);
@@ -1000,8 +997,9 @@
         mWakeUpCoordinator.setIconAreaController(mNotificationIconAreaController);
         inflateShelf();
         mNotificationIconAreaController.setupShelf(mNotificationShelf);
-        mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons);
-        mNotificationPanel.addExpansionListener(mWakeUpCoordinator);
+        mNotificationPanelViewController.setOnReinflationListener(
+                mNotificationIconAreaController::initAodIcons);
+        mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
 
         mDarkIconDispatcher.addDarkReceiver(mNotificationIconAreaController);
         // Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1015,7 +1013,7 @@
                     PhoneStatusBarView oldStatusBarView = mStatusBarView;
                     mStatusBarView = (PhoneStatusBarView) fragment.getView();
                     mStatusBarView.setBar(this);
-                    mStatusBarView.setPanel(mNotificationPanel);
+                    mStatusBarView.setPanel(mNotificationPanelViewController);
                     mStatusBarView.setScrimController(mScrimController);
 
                     // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
@@ -1026,7 +1024,7 @@
                     // it needs to notify PhoneStatusBarView's new instance to update the correct
                     // status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
                     if (mHeadsUpManager.hasPinnedHeadsUp()) {
-                        mNotificationPanel.notifyBarPanelExpansionChanged();
+                        mNotificationPanelViewController.notifyBarPanelExpansionChanged();
                     }
                     mStatusBarView.setBouncerShowing(mBouncerShowing);
                     if (oldStatusBarView != null) {
@@ -1040,10 +1038,12 @@
                         // This view is being recreated, let's destroy the old one
                         mHeadsUpAppearanceController.destroy();
                     }
+                    // TODO: this should probably be scoped to the StatusBarComponent
                     mHeadsUpAppearanceController = new HeadsUpAppearanceController(
                             mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow,
                             mStatusBarStateController, mKeyguardBypassController,
-                            mKeyguardStateController, mWakeUpCoordinator, mCommandQueue);
+                            mKeyguardStateController, mWakeUpCoordinator, mCommandQueue,
+                            mNotificationPanelViewController);
                     mHeadsUpAppearanceController.readFrom(oldController);
 
                     mLightsOutNotifController.setLightsOutNotifView(
@@ -1059,11 +1059,11 @@
         mHeadsUpManager.setUp(mStatusBarWindow, mGroupManager, this, mVisualStabilityManager);
         mConfigurationController.addCallback(mHeadsUpManager);
         mHeadsUpManager.addListener(this);
-        mHeadsUpManager.addListener(mNotificationPanel);
+        mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
         mHeadsUpManager.addListener(mGroupManager);
         mHeadsUpManager.addListener(mGroupAlertTransferHelper);
         mHeadsUpManager.addListener(mVisualStabilityManager);
-        mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
+        mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
         mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
         mNotificationLogger.setHeadsUpManager(mHeadsUpManager);
@@ -1078,7 +1078,8 @@
                 SystemUIFactory.getInstance().createKeyguardIndicationController(mContext,
                         mStatusBarWindow.findViewById(R.id.keyguard_indication_area),
                         mStatusBarWindow.findViewById(R.id.lock_icon));
-        mNotificationPanel.setKeyguardIndicationController(mKeyguardIndicationController);
+        mNotificationPanelViewController.setKeyguardIndicationController(
+                mKeyguardIndicationController);
 
         mAmbientIndicationContainer = mStatusBarWindow.findViewById(
                 R.id.ambient_indication_container);
@@ -1113,19 +1114,19 @@
         });
         mScrimController.attachViews(scrimBehind, scrimInFront, scrimForBubble);
 
-        mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
-                mHeadsUpManager, mNotificationIconAreaController, mScrimController);
+        mNotificationPanelViewController.initDependencies(this, mGroupManager, mNotificationShelf,
+                mNotificationIconAreaController, mScrimController);
 
         BackDropView backdrop = mStatusBarWindow.findViewById(R.id.backdrop);
         mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
                 backdrop.findViewById(R.id.backdrop_back), mScrimController, mLockscreenWallpaper);
 
-        mNotificationPanel.setUserSetupComplete(mUserSetup);
+        mNotificationPanelViewController.setUserSetupComplete(mUserSetup);
         if (UserManager.get(mContext).isUserSwitcherEnabled()) {
             createUserSwitcher();
         }
 
-        mNotificationPanel.setLaunchAffordanceListener(
+        mNotificationPanelViewController.setLaunchAffordanceListener(
                 mLockscreenLockIconController::onShowingLaunchAffordanceChanged);
 
         // Set up the quick settings tile panel
@@ -1139,6 +1140,7 @@
                             .withDefault(this::createDefaultQSFragment)
                             .build());
             mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow,
+                    mNotificationPanelViewController,
                     (visible) -> {
                         mBrightnessMirrorVisible = visible;
                         updateScrimController();
@@ -1232,7 +1234,7 @@
     private void setUpPresenter() {
         // Set up the initial notification state.
         mActivityLaunchAnimator = new ActivityLaunchAnimator(
-                mStatusBarWindowViewController, this, mNotificationPanel,
+                mStatusBarWindowViewController, this, mNotificationPanelViewController,
                 (NotificationListContainer) mStackScroller);
 
         final NotificationRowBinderImpl rowBinder =
@@ -1244,7 +1246,7 @@
                         mNotificationLogger);
 
         // TODO: inject this.
-        mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
+        mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanelViewController,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
                 mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
                 mNotificationAlertingManager, rowBinder, mKeyguardStateController,
@@ -1265,6 +1267,7 @@
                         .setStatusBar(this)
                         .setActivityLaunchAnimator(mActivityLaunchAnimator)
                         .setNotificationPresenter(mPresenter)
+                        .setNotificationPanelViewController(mNotificationPanelViewController)
                         .build();
 
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
@@ -1374,7 +1377,7 @@
         }
         // We need the new R.id.keyguard_indication_area before recreating
         // mKeyguardIndicationController
-        mNotificationPanel.onThemeChanged();
+        mNotificationPanelViewController.onThemeChanged();
         onThemeChanged();
     }
 
@@ -1389,7 +1392,7 @@
         mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
                 mStatusBarWindow.findViewById(R.id.keyguard_user_switcher),
                 mStatusBarWindow.findViewById(R.id.keyguard_header),
-                mNotificationPanel);
+                mNotificationPanelViewController);
     }
 
     private void inflateStatusBarWindow() {
@@ -1398,16 +1401,17 @@
                 .statusBarWindowView(mStatusBarWindow).build();
         mStatusBarWindowViewController = statusBarComponent.getStatusBarWindowViewController();
         mStatusBarWindowViewController.setupExpandedStatusBar();
+        mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
     }
 
     protected void startKeyguard() {
         Trace.beginSection("StatusBar#startKeyguard");
         mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
         mStatusBarKeyguardViewManager.registerStatusBar(
-                /* statusBar= */ this, getBouncerContainer(), mNotificationPanel,
-                mBiometricUnlockController, mDismissCallbackRegistry,
-                mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
-                mKeyguardBypassController, mFalsingManager);
+                /* statusBar= */ this, getBouncerContainer(),
+                mNotificationPanelViewController, mBiometricUnlockController,
+                mDismissCallbackRegistry, mStatusBarWindow.findViewById(R.id.lock_icon_container),
+                mStackScroller, mKeyguardBypassController, mFalsingManager);
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
@@ -1484,7 +1488,7 @@
                 && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
                 && !mDozing
                 && !ONLY_CORE_APPS;
-        mNotificationPanel.setQsExpansionEnabled(expandEnabled);
+        mNotificationPanelViewController.setQsExpansionEnabled(expandEnabled);
         Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
     }
 
@@ -1644,7 +1648,7 @@
 
     public void setQsExpanded(boolean expanded) {
         mStatusBarWindowController.setQsExpanded(expanded);
-        mNotificationPanel.setStatusAccessibilityImportance(expanded
+        mNotificationPanelViewController.setStatusAccessibilityImportance(expanded
                 ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
                 : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
         if (getNavigationBarView() != null) {
@@ -1678,22 +1682,22 @@
         if (inPinnedMode) {
             mStatusBarWindowController.setHeadsUpShowing(true);
             mStatusBarWindowController.setForceStatusBarVisible(true);
-            if (mNotificationPanel.isFullyCollapsed()) {
+            if (mNotificationPanelViewController.isFullyCollapsed()) {
                 // We need to ensure that the touchable region is updated before the window will be
                 // resized, in order to not catch any touches. A layout will ensure that
                 // onComputeInternalInsets will be called and after that we can resize the layout. Let's
                 // make sure that the window stays small for one frame until the touchableRegion is set.
-                mNotificationPanel.requestLayout();
+                mNotificationPanelViewController.getView().requestLayout();
                 mStatusBarWindowController.setForceWindowCollapsed(true);
-                mNotificationPanel.post(() -> {
+                mNotificationPanelViewController.getView().post(() -> {
                     mStatusBarWindowController.setForceWindowCollapsed(false);
                 });
             }
         } else {
             boolean bypassKeyguard = mKeyguardBypassController.getBypassEnabled()
                     && mState == StatusBarState.KEYGUARD;
-            if (!mNotificationPanel.isFullyCollapsed() || mNotificationPanel.isTracking()
-                    || bypassKeyguard) {
+            if (!mNotificationPanelViewController.isFullyCollapsed()
+                    || mNotificationPanelViewController.isTracking() || bypassKeyguard) {
                 // We are currently tracking or is open and the shade doesn't need to be kept
                 // open artificially.
                 mStatusBarWindowController.setHeadsUpShowing(false);
@@ -1704,7 +1708,7 @@
                 // we need to keep the panel open artificially, let's wait until the animation
                 // is finished.
                 mHeadsUpManager.setHeadsUpGoingAway(true);
-                mNotificationPanel.runAfterAnimationFinished(() -> {
+                mNotificationPanelViewController.runAfterAnimationFinished(() -> {
                     if (!mHeadsUpManager.hasPinnedHeadsUp()) {
                         mStatusBarWindowController.setHeadsUpShowing(false);
                         mHeadsUpManager.setHeadsUpGoingAway(false);
@@ -1757,7 +1761,7 @@
     }
 
     public boolean hideStatusBarIconsWhenExpanded() {
-        return mNotificationPanel.hideStatusBarIconsWhenExpanded();
+        return mNotificationPanelViewController.hideStatusBarIconsWhenExpanded();
     }
 
     @Override
@@ -1947,19 +1951,21 @@
 
         if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
-            mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */);
+            mNotificationPanelViewController.collapse(
+                    false /* delayed */, 1.0f /* speedUpFactor */);
         } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
-            if (mNotificationPanel.isFullyCollapsed()) {
+            if (mNotificationPanelViewController.isFullyCollapsed()) {
                 if (mVibrateOnOpening) {
                     mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
                 }
-                mNotificationPanel.expand(true /* animate */);
+                mNotificationPanelViewController.expand(true /* animate */);
                 ((NotificationListContainer) mStackScroller).setWillExpand(true);
                 mHeadsUpManager.unpinAll(true /* userUnpinned */);
                 mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1);
-            } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){
-                mNotificationPanel.flingSettings(0 /* velocity */,
+            } else if (!mNotificationPanelViewController.isInSettings()
+                    && !mNotificationPanelViewController.isExpanding()) {
+                mNotificationPanelViewController.flingSettings(0 /* velocity */,
                         NotificationPanelView.FLING_EXPAND);
                 mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN_QS, 1);
             }
@@ -2054,9 +2060,9 @@
         }
 
         if (start) {
-            mNotificationPanel.startWaitingForOpenPanelGesture();
+            mNotificationPanelViewController.startWaitingForOpenPanelGesture();
         } else {
-            mNotificationPanel.stopWaitingForOpenPanelGesture(velocity);
+            mNotificationPanelViewController.stopWaitingForOpenPanelGesture(velocity);
         }
     }
 
@@ -2067,7 +2073,7 @@
             return ;
         }
 
-        mNotificationPanel.expandWithoutQs();
+        mNotificationPanelViewController.expandWithoutQs();
 
         if (false) postStartTracing();
     }
@@ -2085,7 +2091,7 @@
         if (subPanel != null) {
             mQSPanel.openDetails(subPanel);
         }
-        mNotificationPanel.expandWithQs();
+        mNotificationPanelViewController.expandWithQs();
 
         if (false) postStartTracing();
     }
@@ -2108,7 +2114,7 @@
         mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
                 1.0f /* speedUpFactor */);
 
-        mNotificationPanel.closeQs();
+        mNotificationPanelViewController.closeQs();
 
         mExpandedVisible = false;
         visibilityChanged(false);
@@ -2129,7 +2135,8 @@
             Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
         }
         mCommandQueue.recomputeDisableFlags(
-                mDisplayId, mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
+                mDisplayId,
+                mNotificationPanelViewController.hideStatusBarIconsWhenExpanded() /* animate */);
 
         // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
         // the bouncer appear animation.
@@ -2310,12 +2317,12 @@
                     batteryLevel, new WirelessChargingAnimation.Callback() {
                         @Override
                         public void onAnimationStarting() {
-                            CrossFadeHelper.fadeOut(mNotificationPanel, 1);
+                            CrossFadeHelper.fadeOut(mNotificationPanelViewController.getView(), 1);
                         }
 
                         @Override
                         public void onAnimationEnded() {
-                            CrossFadeHelper.fadeIn(mNotificationPanel);
+                            CrossFadeHelper.fadeIn(mNotificationPanelViewController.getView());
                         }
                     }, mDozing).show();
         } else {
@@ -2344,7 +2351,7 @@
 
     // Called by NavigationBarFragment
     void setQsScrimEnabled(boolean scrimEnabled) {
-        mNotificationPanel.setQsScrimEnabled(scrimEnabled);
+        mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled);
     }
 
     void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState,
@@ -2436,11 +2443,12 @@
         }
 
         pw.println("  Panels: ");
-        if (mNotificationPanel != null) {
-            pw.println("    mNotificationPanel=" +
-                mNotificationPanel + " params=" + mNotificationPanel.getLayoutParams().debug(""));
+        if (mNotificationPanelViewController != null) {
+            pw.println("    mNotificationPanel="
+                    + mNotificationPanelViewController.getView() + " params="
+                    + mNotificationPanelViewController.getView().getLayoutParams().debug(""));
             pw.print  ("      ");
-            mNotificationPanel.dump(fd, pw, args);
+            mNotificationPanelViewController.dump(fd, pw, args);
         }
         pw.println("  mStackScroller: ");
         if (mStackScroller instanceof Dumpable) {
@@ -2653,7 +2661,8 @@
                     // Do it after DismissAction has been processed to conserve the needed ordering.
                     mHandler.post(mShadeController::runPostCollapseRunnables);
                 }
-            } else if (isInLaunchTransition() && mNotificationPanel.isLaunchTransitionFinished()) {
+            } else if (isInLaunchTransition()
+                    && mNotificationPanelViewController.isLaunchTransitionFinished()) {
 
                 // We are not dismissing the shade, but the launch transition is already finished,
                 // so nobody will call readyForKeyguardDone anymore. Post it such that
@@ -2810,8 +2819,8 @@
         if (mStatusBarView != null) {
             mStatusBarView.updateResources();
         }
-        if (mNotificationPanel != null) {
-            mNotificationPanel.updateResources();
+        if (mNotificationPanelViewController != null) {
+            mNotificationPanelViewController.updateResources();
         }
         if (mBrightnessMirrorController != null) {
             mBrightnessMirrorController.updateResources();
@@ -3103,7 +3112,7 @@
     public void showKeyguardImpl() {
         mIsKeyguard = true;
         if (mKeyguardStateController.isLaunchTransitionFadingAway()) {
-            mNotificationPanel.animate().cancel();
+            mNotificationPanelViewController.cancelAnimation();
             onLaunchTransitionFadingEnded();
         }
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
@@ -3130,8 +3139,8 @@
     }
 
     private void onLaunchTransitionFadingEnded() {
-        mNotificationPanel.setAlpha(1.0f);
-        mNotificationPanel.onAffordanceLaunchEnded();
+        mNotificationPanelViewController.setAlpha(1.0f);
+        mNotificationPanelViewController.onAffordanceLaunchEnded();
         releaseGestureWakeLock();
         runLaunchTransitionEndRunnable();
         mKeyguardStateController.setLaunchTransitionFadingAway(false);
@@ -3139,8 +3148,8 @@
     }
 
     public boolean isInLaunchTransition() {
-        return mNotificationPanel.isLaunchTransitionRunning()
-                || mNotificationPanel.isLaunchTransitionFinished();
+        return mNotificationPanelViewController.isLaunchTransitionRunning()
+                || mNotificationPanelViewController.isLaunchTransitionFinished();
     }
 
     /**
@@ -3161,18 +3170,15 @@
             }
             updateScrimController();
             mPresenter.updateMediaMetaData(false, true);
-            mNotificationPanel.setAlpha(1);
-            mNotificationPanel.animate()
-                    .alpha(0)
-                    .setStartDelay(FADE_KEYGUARD_START_DELAY)
-                    .setDuration(FADE_KEYGUARD_DURATION)
-                    .withLayer()
-                    .withEndAction(this::onLaunchTransitionFadingEnded);
+            mNotificationPanelViewController.setAlpha(1);
+            mNotificationPanelViewController.fadeOut(
+                    FADE_KEYGUARD_START_DELAY, FADE_KEYGUARD_DURATION,
+                    this::onLaunchTransitionFadingEnded);
             mCommandQueue.appTransitionStarting(mDisplayId, SystemClock.uptimeMillis(),
                     LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true);
         };
-        if (mNotificationPanel.isLaunchTransitionRunning()) {
-            mNotificationPanel.setLaunchTransitionEndRunnable(hideRunnable);
+        if (mNotificationPanelViewController.isLaunchTransitionRunning()) {
+            mNotificationPanelViewController.setLaunchTransitionEndRunnable(hideRunnable);
         } else {
             hideRunnable.run();
         }
@@ -3183,22 +3189,18 @@
      * fading.
      */
     public void fadeKeyguardWhilePulsing() {
-        mNotificationPanel.animate()
-                .alpha(0f)
-                .setStartDelay(0)
-                .setDuration(FADE_KEYGUARD_DURATION_PULSING)
-                .setInterpolator(Interpolators.ALPHA_OUT)
-                .withEndAction(()-> {
-                    hideKeyguard();
-                    mStatusBarKeyguardViewManager.onKeyguardFadedAway();
-                }).start();
+        mNotificationPanelViewController.fadeOut(0, FADE_KEYGUARD_DURATION_PULSING,
+                ()-> {
+                hideKeyguard();
+                mStatusBarKeyguardViewManager.onKeyguardFadedAway();
+            }).start();
     }
 
     /**
      * Plays the animation when an activity that was occluding Keyguard goes away.
      */
     public void animateKeyguardUnoccluding() {
-        mNotificationPanel.setExpandedFraction(0f);
+        mNotificationPanelViewController.setExpandedFraction(0f);
         animateExpandNotificationsPanel();
     }
 
@@ -3214,9 +3216,9 @@
 
     private void onLaunchTransitionTimeout() {
         Log.w(TAG, "Launch transition: Timeout!");
-        mNotificationPanel.onAffordanceLaunchEnded();
+        mNotificationPanelViewController.onAffordanceLaunchEnded();
         releaseGestureWakeLock();
-        mNotificationPanel.resetViews(false /* animate */);
+        mNotificationPanelViewController.resetViews(false /* animate */);
     }
 
     private void runLaunchTransitionEndRunnable() {
@@ -3249,7 +3251,7 @@
                 mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
             }
             long delay = mKeyguardStateController.calculateGoingToFullShadeDelay();
-            mNotificationPanel.animateToFullShade(delay);
+            mNotificationPanelViewController.animateToFullShade(delay);
             if (mDraggedDownEntry != null) {
                 mDraggedDownEntry.setUserLocked(false);
                 mDraggedDownEntry = null;
@@ -3258,7 +3260,7 @@
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
             mNavigationBarController.disableAnimationsDuringHide(mDisplayId, delay);
-        } else if (!mNotificationPanel.isCollapsing()) {
+        } else if (!mNotificationPanelViewController.isCollapsing()) {
             instantCollapseNotificationPanel();
         }
 
@@ -3269,10 +3271,10 @@
         }
         mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
         releaseGestureWakeLock();
-        mNotificationPanel.onAffordanceLaunchEnded();
-        mNotificationPanel.animate().cancel();
-        mNotificationPanel.setAlpha(1f);
-        ViewGroupFadeHelper.reset(mNotificationPanel);
+        mNotificationPanelViewController.onAffordanceLaunchEnded();
+        mNotificationPanelViewController.cancelAnimation();
+        mNotificationPanelViewController.setAlpha(1f);
+        mNotificationPanelViewController.resetViewGroupFade();
         updateScrimController();
         Trace.endSection();
         return staying;
@@ -3347,7 +3349,7 @@
         boolean animate = (!mDozing && mDozeServiceHost.shouldAnimateWakeup() && !wakeAndUnlock)
                 || (mDozing && mDozeServiceHost.shouldAnimateScreenOff() && sleepingFromKeyguard);
 
-        mNotificationPanel.setDozing(mDozing, animate, mWakeUpTouchLocation);
+        mNotificationPanelViewController.setDozing(mDozing, animate, mWakeUpTouchLocation);
         updateQsExpansionEnabled();
         Trace.endSection();
     }
@@ -3379,27 +3381,27 @@
 
     public void endAffordanceLaunch() {
         releaseGestureWakeLock();
-        mNotificationPanel.onAffordanceLaunchEnded();
+        mNotificationPanelViewController.onAffordanceLaunchEnded();
     }
 
     public boolean onBackPressed() {
         boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED;
         if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) {
             if (!isScrimmedBouncer) {
-                mNotificationPanel.expandWithoutQs();
+                mNotificationPanelViewController.expandWithoutQs();
             }
             return true;
         }
-        if (mNotificationPanel.isQsExpanded()) {
-            if (mNotificationPanel.isQsDetailShowing()) {
-                mNotificationPanel.closeQsDetail();
+        if (mNotificationPanelViewController.isQsExpanded()) {
+            if (mNotificationPanelViewController.isQsDetailShowing()) {
+                mNotificationPanelViewController.closeQsDetail();
             } else {
-                mNotificationPanel.animateCloseQs(false /* animateAway */);
+                mNotificationPanelViewController.animateCloseQs(false /* animateAway */);
             }
             return true;
         }
         if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) {
-            if (mNotificationPanel.canPanelBeCollapsed()) {
+            if (mNotificationPanelViewController.canPanelBeCollapsed()) {
                 mShadeController.animateCollapsePanels();
             } else {
                 mBubbleController.performBackPressIfNeeded();
@@ -3429,7 +3431,7 @@
     }
 
     void instantCollapseNotificationPanel() {
-        mNotificationPanel.instantCollapse();
+        mNotificationPanelViewController.instantCollapse();
         mShadeController.runPostCollapseRunnables();
     }
 
@@ -3491,12 +3493,11 @@
     public void onDozingChanged(boolean isDozing) {
         Trace.beginSection("StatusBar#updateDozing");
         mDozing = isDozing;
-        mDozeServiceHost.setDozing(mDozing);
 
         // Collapse the notification panel if open
         boolean dozingAnimated = mDozeServiceHost.getDozingRequested()
                 && mDozeParameters.shouldControlScreenOff();
-        mNotificationPanel.resetViews(dozingAnimated);
+        mNotificationPanelViewController.resetViews(dozingAnimated);
 
         updateQsExpansionEnabled();
         mKeyguardViewMediator.setDozing(mDozing);
@@ -3570,7 +3571,7 @@
      * @return bottom area view
      */
     public KeyguardBottomAreaView getKeyguardBottomAreaView() {
-        return mNotificationPanel.getKeyguardBottomAreaView();
+        return mNotificationPanelViewController.getKeyguardBottomAreaView();
     }
 
     /**
@@ -3609,7 +3610,7 @@
             mDraggedDownEntry = entry;
             mPendingRemoteInputView = null;
         } else {
-            mNotificationPanel.animateToFullShade(0 /* delay */);
+            mNotificationPanelViewController.animateToFullShade(0 /* delay */);
             mStatusBarStateController.setState(StatusBarState.SHADE_LOCKED);
         }
     }
@@ -3635,7 +3636,7 @@
      * Collapses the notification shade if it is tracking or expanded.
      */
     public void collapseShade() {
-        if (mNotificationPanel.isTracking()) {
+        if (mNotificationPanelViewController.isTracking()) {
             mStatusBarWindowViewController.cancelCurrentTouch();
         }
         if (mPanelExpanded && mState == StatusBarState.SHADE) {
@@ -3647,7 +3648,7 @@
     final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
         @Override
         public void onFinishedGoingToSleep() {
-            mNotificationPanel.onAffordanceLaunchEnded();
+            mNotificationPanelViewController.onAffordanceLaunchEnded();
             releaseGestureWakeLock();
             mLaunchCameraWhenFinishedWaking = false;
             mDeviceInteractive = false;
@@ -3708,7 +3709,8 @@
             mBypassHeadsUpNotifier.setFullyAwake(true);
             mWakeUpCoordinator.setWakingUp(false);
             if (mLaunchCameraWhenFinishedWaking) {
-                mNotificationPanel.launchCamera(false /* animate */, mLastCameraLaunchSource);
+                mNotificationPanelViewController.launchCamera(
+                        false /* animate */, mLastCameraLaunchSource);
                 mLaunchCameraWhenFinishedWaking = false;
             }
             updateScrimController();
@@ -3725,7 +3727,7 @@
                 && !mDozeParameters.shouldControlScreenOff();
         boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
                 || goingToSleepWithoutAnimation;
-        mNotificationPanel.setTouchAndAnimationDisabled(disabled);
+        mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
 
@@ -3733,7 +3735,7 @@
         @Override
         public void onScreenTurningOn() {
             mFalsingManager.onScreenTurningOn();
-            mNotificationPanel.onScreenTurningOn();
+            mNotificationPanelViewController.onScreenTurningOn();
         }
 
         @Override
@@ -3802,7 +3804,7 @@
             mLaunchCameraOnFinishedGoingToSleep = true;
             return;
         }
-        if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+        if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
             if (DEBUG_CAMERA_LIFT) Slog.d(TAG, "Can't launch camera right now");
             return;
         }
@@ -3832,7 +3834,8 @@
                 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                     mStatusBarKeyguardViewManager.reset(true /* hide */);
                 }
-                mNotificationPanel.launchCamera(mDeviceInteractive /* animate */, source);
+                mNotificationPanelViewController.launchCamera(
+                        mDeviceInteractive /* animate */, source);
                 updateScrimController();
             } else {
                 // We need to defer the camera launch until the screen comes on, since otherwise
@@ -3891,7 +3894,7 @@
                 !mBiometricUnlockController.isBiometricUnlock());
 
         boolean launchingAffordanceWithPreview =
-                mNotificationPanel.isLaunchingAffordanceWithPreview();
+                mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
 
         if (mBouncerShowing) {
@@ -4240,7 +4243,7 @@
      * When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
      */
     public void onBouncerPreHideAnimation() {
-        mNotificationPanel.onBouncerPreHideAnimation();
+        mNotificationPanelViewController.onBouncerPreHideAnimation();
         mLockscreenLockIconController.onBouncerPreHideAnimation();
     }
 
@@ -4283,8 +4286,8 @@
         mAssistManagerLazy.get().showDisclosure();
     }
 
-    public NotificationPanelView getPanel() {
-        return mNotificationPanel;
+    public NotificationPanelViewController getPanelController() {
+        return mNotificationPanelViewController;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f51174b..407d256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -133,7 +133,7 @@
     protected LockPatternUtils mLockPatternUtils;
     protected ViewMediatorCallback mViewMediatorCallback;
     protected StatusBar mStatusBar;
-    private NotificationPanelView mNotificationPanelView;
+    private NotificationPanelViewController mNotificationPanelViewController;
     private BiometricUnlockController mBiometricUnlockController;
 
     private ViewGroup mContainer;
@@ -224,7 +224,7 @@
 
     public void registerStatusBar(StatusBar statusBar,
             ViewGroup container,
-            NotificationPanelView notificationPanelView,
+            NotificationPanelViewController notificationPanelViewController,
             BiometricUnlockController biometricUnlockController,
             DismissCallbackRegistry dismissCallbackRegistry,
             ViewGroup lockIconContainer, View notificationContainer,
@@ -239,8 +239,8 @@
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
                 mExpansionCallback, mKeyguardStateController, falsingManager, bypassController);
-        mNotificationPanelView = notificationPanelView;
-        notificationPanelView.addExpansionListener(this);
+        mNotificationPanelViewController = notificationPanelViewController;
+        notificationPanelViewController.addExpansionListener(this);
         mBypassController = bypassController;
         mNotificationContainer = notificationContainer;
     }
@@ -253,7 +253,7 @@
         // • The user quickly taps on the display and we show "swipe up to unlock."
         // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
         // • Full-screen user switcher is displayed.
-        if (mNotificationPanelView.isUnlockHintRunning()) {
+        if (mNotificationPanelViewController.isUnlockHintRunning()) {
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
         } else if (bouncerNeedsScrimming()) {
             mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
@@ -284,7 +284,7 @@
             return;
         }
         boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD
-                && !mNotificationPanelView.isQsExpanded();
+                && !mNotificationPanelViewController.isQsExpanded();
         boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs)
                 && !mBouncer.isAnimatingAway() && !mKeyguardStateController.isKeyguardFadingAway();
 
@@ -555,7 +555,7 @@
         } else if (finishRunnable != null) {
             finishRunnable.run();
         }
-        mNotificationPanelView.blockExpansionForCurrentTouch();
+        mNotificationPanelViewController.blockExpansionForCurrentTouch();
         updateLockIcon();
     }
 
@@ -609,7 +609,8 @@
             hideBouncer(true /* destroyView */);
             if (wakeUnlockPulsing) {
                 if (needsFading) {
-                    ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
+                    ViewGroupFadeHelper.fadeOutAllChildrenExcept(
+                            mNotificationPanelViewController.getView(),
                             mNotificationContainer,
                             fadeoutDuration,
                                     () -> {
@@ -625,7 +626,8 @@
                 if (!staying) {
                     mStatusBarWindowController.setKeyguardFadingAway(true);
                     if (needsFading) {
-                        ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView,
+                        ViewGroupFadeHelper.fadeOutAllChildrenExcept(
+                                mNotificationPanelViewController.getView(),
                                 mNotificationContainer,
                                 fadeoutDuration,
                                 () -> {
@@ -684,7 +686,7 @@
     public void onKeyguardFadedAway() {
         mContainer.postDelayed(() -> mStatusBarWindowController.setKeyguardFadingAway(false),
                 100);
-        ViewGroupFadeHelper.reset(mNotificationPanelView);
+        ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
         mStatusBar.finishKeyguardFadingAway();
         mBiometricUnlockController.finishKeyguardFadingAway();
         WindowManagerGlobal.getInstance().trimMemory(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 12033de..153ca22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 661a7b1..0f3b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -61,7 +61,6 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -102,7 +101,7 @@
     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
     private final MetricsLogger mMetricsLogger;
     private final Context mContext;
-    private final NotificationPanelView mNotificationPanel;
+    private final NotificationPanelViewController mNotificationPanel;
     private final NotificationPresenter mPresenter;
     private final LockPatternUtils mLockPatternUtils;
     private final HeadsUpManagerPhone mHeadsUpManager;
@@ -121,7 +120,7 @@
     private boolean mIsCollapsingToShowActivityOverLockscreen;
 
     private StatusBarNotificationActivityStarter(Context context, CommandQueue commandQueue,
-            Lazy<AssistManager> assistManagerLazy, NotificationPanelView panel,
+            Lazy<AssistManager> assistManagerLazy, NotificationPanelViewController panel,
             NotificationPresenter presenter, NotificationEntryManager entryManager,
             HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter,
             ActivityLaunchAnimator activityLaunchAnimator, IStatusBarService statusBarService,
@@ -519,7 +518,6 @@
         private final NotificationGroupManager mGroupManager;
         private final NotificationLockscreenUserManager mLockscreenUserManager;
         private final KeyguardStateController mKeyguardStateController;
-        private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
         private final MetricsLogger mMetricsLogger;
         private final LockPatternUtils mLockPatternUtils;
         private final Handler mMainThreadHandler;
@@ -527,7 +525,8 @@
         private final Executor mUiBgExecutor;
         private final ActivityIntentHelper mActivityIntentHelper;
         private final BubbleController mBubbleController;
-        private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+        private NotificationPanelViewController mNotificationPanelViewController;
+        private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
         private final ShadeController mShadeController;
         private NotificationPresenter mNotificationPresenter;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -558,8 +557,7 @@
                 @UiBackground Executor uiBgExecutor,
                 ActivityIntentHelper activityIntentHelper,
                 BubbleController bubbleController,
-                ShadeController shadeController,
-                SuperStatusBarViewFactory superStatusBarViewFactory) {
+                ShadeController shadeController) {
             mContext = context;
             mCommandQueue = commandQueue;
             mAssistManagerLazy = assistManagerLazy;
@@ -585,7 +583,6 @@
             mActivityIntentHelper = activityIntentHelper;
             mBubbleController = bubbleController;
             mShadeController = shadeController;
-            mSuperStatusBarViewFactory = superStatusBarViewFactory;
         }
 
         /** Sets the status bar to use as {@link StatusBar}. */
@@ -604,10 +601,19 @@
             return this;
         }
 
+        /** Set the NotificationPanelViewController */
+        public Builder setNotificationPanelViewController(
+                NotificationPanelViewController notificationPanelViewController) {
+            mNotificationPanelViewController = notificationPanelViewController;
+            return this;
+        }
+
+
+
         public StatusBarNotificationActivityStarter build() {
             return new StatusBarNotificationActivityStarter(mContext,
                     mCommandQueue, mAssistManagerLazy,
-                    mSuperStatusBarViewFactory.getNotificationPanelView(),
+                    mNotificationPanelViewController,
                     mNotificationPresenter,
                     mEntryManager,
                     mHeadsUpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index beb4579..0fd0dab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -107,7 +107,7 @@
     private final NotificationGutsManager mGutsManager =
             Dependency.get(NotificationGutsManager.class);
 
-    private final NotificationPanelView mNotificationPanel;
+    private final NotificationPanelViewController mNotificationPanel;
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final AboveShelfObserver mAboveShelfObserver;
     private final DozeScrimController mDozeScrimController;
@@ -132,7 +132,7 @@
     private int mMaxKeyguardNotifications;
 
     public StatusBarNotificationPresenter(Context context,
-            NotificationPanelView panel,
+            NotificationPanelViewController panel,
             HeadsUpManagerPhone headsUp,
             StatusBarWindowView statusBarWindow,
             ViewGroup stackScroller,
@@ -172,7 +172,7 @@
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
         if (MULTIUSER_DEBUG) {
-            mNotificationPanelDebugText = mNotificationPanel.findViewById(R.id.header_debug_info);
+            mNotificationPanelDebugText = mNotificationPanel.getHeaderDebugInfo();
             mNotificationPanelDebugText.setVisibility(View.VISIBLE);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 6b51391..1e3c5d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -75,6 +75,10 @@
         setMotionEventSplittingEnabled(false);
     }
 
+    public NotificationPanelView getNotificationPanelView() {
+        return findViewById(R.id.notification_panel);
+    }
+
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
         final Insets insets = windowInsets.getMaxInsets(WindowInsets.Type.systemBars());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index eb86bcc..4935f0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -26,11 +26,9 @@
 import android.view.GestureDetector;
 import android.view.InputDevice;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewStub;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.ExpandHelper;
@@ -93,6 +91,7 @@
     private boolean mSingleTapEnabled;
     private boolean mExpandingBelowNotch;
     private final DockManager mDockManager;
+    private final NotificationPanelViewController mNotificationPanelViewController;
 
     @Inject
     public StatusBarWindowViewController(
@@ -113,7 +112,8 @@
             CommandQueue commandQueue,
             ShadeController shadeController,
             DockManager dockManager,
-            StatusBarWindowView statusBarWindowView) {
+            StatusBarWindowView statusBarWindowView,
+            NotificationPanelViewController notificationPanelViewController) {
         mInjectionInflationController = injectionInflationController;
         mCoordinator = coordinator;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -132,6 +132,7 @@
         mView = statusBarWindowView;
         mShadeController = shadeController;
         mDockManager = dockManager;
+        mNotificationPanelViewController = notificationPanelViewController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -139,39 +140,6 @@
 
     /** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
     public void setupExpandedStatusBar() {
-        // TODO: create controller for NotificationPanelView
-        NotificationPanelView notificationPanelView = new NotificationPanelView(
-                mView.getContext(),
-                null,
-                mInjectionInflationController,
-                mCoordinator,
-                mPulseExpansionHandler,
-                mDynamicPrivacyController,
-                mBypassController,
-                mFalsingManager,
-                mPluginManager,
-                mShadeController,
-                mNotificationLockscreenUserManager,
-                mNotificationEntryManager,
-                mKeyguardStateController,
-                mStatusBarStateController,
-                mDozeLog,
-                mDozeParameters,
-                mCommandQueue);
-        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-        notificationPanelView.setVisibility(View.INVISIBLE);
-        notificationPanelView.setId(R.id.notification_panel);
-        LayoutInflater li = mInjectionInflationController.injectable(
-                LayoutInflater.from(mView.getContext()));
-
-        li.inflate(R.layout.status_bar_expanded, notificationPanelView);
-        notificationPanelView.onChildrenAttached();
-
-        ViewStub statusBarExpanded = mView.findViewById(R.id.status_bar_expanded);
-        mView.addView(notificationPanelView, mView.indexOfChild(statusBarExpanded), lp);
-        mView.removeView(statusBarExpanded);
-
         mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
 
         TunerService.Tunable tunable = (key, newValue) -> {
@@ -233,8 +201,8 @@
                 if (!isCancel && mService.shouldIgnoreTouch()) {
                     return false;
                 }
-                if (isDown && notificationPanelView.isFullyCollapsed()) {
-                    notificationPanelView.startExpandLatencyTracking();
+                if (isDown && mNotificationPanelViewController.isFullyCollapsed()) {
+                    mNotificationPanelViewController.startExpandLatencyTracking();
                 }
                 if (isDown) {
                     setTouchActive(true);
@@ -287,7 +255,7 @@
                     return true;
                 }
                 boolean intercept = false;
-                if (notificationPanelView.isFullyExpanded()
+                if (mNotificationPanelViewController.isFullyExpanded()
                         && mDragDownHelper.isDragDownEnabled()
                         && !mService.isBouncerShowing()
                         && !mStatusBarStateController.isDozing()) {
@@ -303,7 +271,7 @@
                 MotionEvent cancellation = MotionEvent.obtain(ev);
                 cancellation.setAction(MotionEvent.ACTION_CANCEL);
                 mStackScrollLayout.onInterceptTouchEvent(cancellation);
-                notificationPanelView.onInterceptTouchEvent(cancellation);
+                mNotificationPanelViewController.getView().onInterceptTouchEvent(cancellation);
                 cancellation.recycle();
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
similarity index 74%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
index f3c843c..21d0bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
+
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
@@ -29,7 +33,8 @@
 /**
  * Dagger subcomponent tied to the lifecycle of StatusBar views.
  */
-@Subcomponent
+@Subcomponent(modules = {StatusBarViewModule.class})
+@StatusBarComponent.StatusBarScope
 public interface StatusBarComponent {
     /**
      * Builder for {@link StatusBarComponent}.
@@ -54,4 +59,10 @@
     @StatusBarScope
     StatusBarWindowViewController getStatusBarWindowViewController();
 
+    /**
+     * Creates a NotificationPanelViewController.
+     */
+    @StatusBarScope
+    NotificationPanelViewController getNotificationPanelViewController();
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
new file mode 100644
index 0000000..20bd51d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone.dagger;
+
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public abstract class StatusBarViewModule {
+    /** */
+    @Provides
+    @StatusBarComponent.StatusBarScope
+    public static NotificationPanelView getNotificationPanelView(
+            StatusBarWindowView statusBarWindowView) {
+        return statusBarWindowView.getNotificationPanelView();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 738d076..cf9d8e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -38,6 +38,11 @@
     void setPowerSaveMode(boolean powerSave);
 
     /**
+     * Returns {@code true} if the device is currently plugged in.
+     */
+    boolean isPluggedIn();
+
+    /**
      * Returns {@code true} if the device is currently in power save mode.
      */
     boolean isPowerSave();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index f132058..d3e6f53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -193,6 +193,11 @@
     }
 
     @Override
+    public boolean isPluggedIn() {
+        return mPluggedIn;
+    }
+
+    @Override
     public boolean isPowerSave() {
         return mPowerSave;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index b198678..625d884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -23,11 +23,11 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-import com.android.internal.util.Preconditions;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 
+import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
@@ -38,16 +38,17 @@
 
     private final StatusBarWindowView mStatusBarWindow;
     private final Consumer<Boolean> mVisibilityCallback;
-    private final NotificationPanelView mNotificationPanel;
+    private final NotificationPanelViewController mNotificationPanel;
     private final ArraySet<BrightnessMirrorListener> mBrightnessMirrorListeners = new ArraySet<>();
     private final int[] mInt2Cache = new int[2];
     private View mBrightnessMirror;
 
     public BrightnessMirrorController(StatusBarWindowView statusBarWindow,
+            NotificationPanelViewController notificationPanelViewController,
             @NonNull Consumer<Boolean> visibilityCallback) {
         mStatusBarWindow = statusBarWindow;
         mBrightnessMirror = statusBarWindow.findViewById(R.id.brightness_mirror);
-        mNotificationPanel = statusBarWindow.findViewById(R.id.notification_panel);
+        mNotificationPanel = notificationPanelViewController;
         mNotificationPanel.setPanelAlphaEndAction(() -> {
             mBrightnessMirror.setVisibility(View.INVISIBLE);
         });
@@ -117,7 +118,7 @@
 
     @Override
     public void addCallback(BrightnessMirrorListener listener) {
-        Preconditions.checkNotNull(listener);
+        Objects.requireNonNull(listener);
         mBrightnessMirrorListeners.add(listener);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 1cb2bd4..0ab08a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -27,7 +27,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -36,6 +35,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Objects;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -98,7 +98,7 @@
 
     @Override
     public void addCallback(@NonNull Callback callback) {
-        Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
+        Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
         if (!mCallbacks.contains(callback)) {
             mCallbacks.add(callback);
         }
@@ -106,7 +106,7 @@
 
     @Override
     public void removeCallback(@NonNull Callback callback) {
-        Preconditions.checkNotNull(callback, "Callback must not be null. b/128895449");
+        Objects.requireNonNull(callback, "Callback must not be null. b/128895449");
         mCallbacks.remove(callback);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 4b283fed..d28a6699 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -35,7 +35,7 @@
 import com.android.systemui.R;
 import com.android.systemui.qs.tiles.UserDetailItemView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 
 /**
  * Manages the user switcher on the Keyguard.
@@ -57,7 +57,8 @@
     private boolean mAnimating;
 
     public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
-            KeyguardStatusBarView statusBarView, NotificationPanelView panelView) {
+            KeyguardStatusBarView statusBarView,
+            NotificationPanelViewController panelViewController) {
         boolean keyguardUserSwitcherEnabled =
                 context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON;
         UserSwitcherController userSwitcherController = Dependency.get(UserSwitcherController.class);
@@ -67,7 +68,7 @@
             reinflateViews();
             mStatusBarView = statusBarView;
             mStatusBarView.setKeyguardUserSwitcher(this);
-            panelView.setKeyguardUserSwitcher(this);
+            panelViewController.setKeyguardUserSwitcher(this);
             mAdapter = new Adapter(context, userSwitcherController, this);
             mAdapter.registerDataSetObserver(mDataSetObserver);
             mUserSwitcherController = userSwitcherController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 6759020..6b842d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -398,7 +398,7 @@
                     intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false),
                     intent.getStringExtra(TelephonyIntents.EXTRA_PLMN));
             notifyListenersIfNecessary();
-        } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
+        } else if (action.equals(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
             updateDataSim();
             notifyListenersIfNecessary();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f20a47b..f640d03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -318,9 +318,9 @@
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+        filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+        filter.addAction(Intent.ACTION_SERVICE_STATE);
         filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
@@ -512,11 +512,11 @@
                 refreshLocale();
                 updateAirplaneMode(false);
                 break;
-            case TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
+            case TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED:
                 // We are using different subs now, we might be able to make calls.
                 recalculateEmergency();
                 break;
-            case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+            case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
                 // Notify every MobileSignalController so they can know whether they are the
                 // data sim or not.
                 for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -534,7 +534,7 @@
                 // Might have different subscriptions now.
                 updateMobileControllers();
                 break;
-            case TelephonyIntents.ACTION_SERVICE_STATE_CHANGED:
+            case Intent.ACTION_SERVICE_STATE:
                 mLastServiceState = ServiceState.newFromBundle(intent.getExtras());
                 if (mMobileSignalControllers.size() == 0) {
                     // If none of the subscriptions are active, we might need to recalculate
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index e7d1c95..718522c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -85,7 +85,8 @@
         boolean visibleWhenEnabled = mContext.getResources().getBoolean(
                 R.bool.config_showWifiIndicatorWhenEnabled);
         boolean wifiVisible = mCurrentState.enabled
-                && (mCurrentState.connected || !mHasMobileData || visibleWhenEnabled);
+                && ((mCurrentState.connected && mCurrentState.inetCondition == 1)
+                    || !mHasMobileData || visibleWhenEnabled);
         String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
         boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
         String contentDescription = getStringIfExists(getContentDescription());
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 6976649..56aae17 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -35,7 +35,6 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.LockIcon;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -129,11 +128,6 @@
         NotificationStackScrollLayout createNotificationStackScrollLayout();
 
         /**
-         * Creates the NotificationPanelView.
-         */
-        NotificationPanelView createPanelView();
-
-        /**
          * Creates the Shelf.
          */
         NotificationShelf creatNotificationShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/SettableWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/SettableWakeLock.java
index a797287..37da236 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/SettableWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/SettableWakeLock.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.util.wakelock;
 
-import com.android.internal.util.Preconditions;
+import java.util.Objects;
 
 public class SettableWakeLock {
 
@@ -26,7 +26,7 @@
     private boolean mAcquired;
 
     public SettableWakeLock(WakeLock inner, String why) {
-        Preconditions.checkNotNull(inner, "inner wakelock required");
+        Objects.requireNonNull(inner, "inner wakelock required");
 
         mInner = inner;
         mWhy = why;
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 4bf1e1c..40ea6dd 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -204,7 +204,7 @@
     @Test
     public void testTelephonyCapable_SimInvalid_ServiceState_InService() {
         // SERVICE_STATE - IN_SERVICE, but SIM_STATE is invalid TelephonyCapable should be False
-        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
@@ -219,7 +219,7 @@
     public void testTelephonyCapable_SimValid_ServiceState_PowerOff() {
         // Simulate AirplaneMode case, SERVICE_STATE - POWER_OFF, check TelephonyCapable False
         // Only receive ServiceState callback IN_SERVICE -> OUT_OF_SERVICE -> POWER_OFF
-        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
         intent.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_LOADED);
         Bundle data = new Bundle();
@@ -241,7 +241,7 @@
      */
     @Test
     public void testTelephonyCapable_BootInitState_ServiceState_OutOfService() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_OUT_OF_SERVICE);
@@ -285,7 +285,7 @@
 
     @Test
     public void testTelephonyCapable_BootInitState_ServiceState_PowerOff() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_POWER_OFF);
@@ -298,7 +298,7 @@
 
     @Test
     public void testTelephonyCapable_SimValid_ServiceState_InService() {
-        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
         Bundle data = new Bundle();
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
@@ -321,10 +321,10 @@
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
                 , putPhoneInfo(intentSimState, data, true));
         mTestableLooper.processAllMessages();
-        // Even SimState Loaded, still need ACTION_SERVICE_STATE_CHANGED turn on mTelephonyCapable
+        // Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
         assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
 
-        Intent intentServiceState =  new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intentServiceState =  new Intent(Intent.ACTION_SERVICE_STATE);
         intentSimState.putExtra(Intent.EXTRA_SIM_STATE
                 , Intent.SIM_STATE_LOADED);
         mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
index a19d1df..25efd32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java
@@ -21,16 +21,17 @@
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertThat;
 
-import android.os.Handler;
 import android.provider.DeviceConfig;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.brightline.BrightLineFalsingManager;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.DeviceConfigProxyFake;
@@ -47,24 +48,22 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class FalsingManagerProxyTest extends SysuiTestCase {
     @Mock(stubOnly = true)
     PluginManager mPluginManager;
     @Mock(stubOnly = true)
     ProximitySensor mProximitySensor;
-    private Handler mHandler;
     private FalsingManagerProxy mProxy;
     private DeviceConfigProxy mDeviceConfig;
-    private TestableLooper mTestableLooper;
+    private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+    private DockManager mDockManager = new DockManagerFake();
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
-        mTestableLooper = TestableLooper.get(this);
-        mHandler = new Handler(mTestableLooper.getLooper());
         mDeviceConfig = new DeviceConfigProxyFake();
         mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
@@ -79,8 +78,8 @@
 
     @Test
     public void test_brightLineFalsingManagerDisabled() {
-        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
-                mDeviceConfig, mUiBgExecutor);
+        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+                mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
     }
 
@@ -88,27 +87,27 @@
     public void test_brightLineFalsingManagerEnabled() throws InterruptedException {
         mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        mTestableLooper.processAllMessages();
-        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
-                mDeviceConfig, mUiBgExecutor);
+        mExecutor.runAllReady();
+        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+                mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class));
     }
 
     @Test
     public void test_brightLineFalsingManagerToggled() throws InterruptedException {
-        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mProximitySensor,
-                mDeviceConfig, mUiBgExecutor);
+        mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mExecutor, mDisplayMetrics,
+                mProximitySensor, mDeviceConfig, mDockManager, mUiBgExecutor);
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
 
         mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false);
-        mTestableLooper.processAllMessages();
+        mExecutor.runAllReady();
         assertThat(mProxy.getInternalFalsingManager(),
                 instanceOf(BrightLineFalsingManager.class));
 
         mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
                 BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false);
-        mTestableLooper.processAllMessages();
+        mExecutor.runAllReady();
         assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
index afe4200..e88ff2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -39,7 +38,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class DiagonalClassifierTest extends ClassifierTest {
 
     // Next variable is not actually five, but is very close. 5 degrees is currently the value
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
index f0f5fc7..44454d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertThat;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -33,7 +32,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class DistanceClassifierTest extends ClassifierTest {
 
     private FalsingDataProvider mDataProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
index 748c137..448c2f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertThat;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 
@@ -36,7 +35,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class FalsingDataProviderTest extends ClassifierTest {
 
     private FalsingDataProvider mDataProvider;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
index 96b2028..4f8e7c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java
@@ -22,7 +22,6 @@
 import static org.junit.Assert.assertThat;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
@@ -34,7 +33,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class PointerCountClassifierTest extends ClassifierTest {
 
     private FalsingClassifier mClassifier;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
index 35d59c1..5b32a394 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
@@ -41,7 +40,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class ProximityClassifierTest extends ClassifierTest {
 
     private static final long NS_PER_MS = 1000000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
index 0355dc3..4346e7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java
@@ -30,7 +30,6 @@
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -42,7 +41,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class TypeClassifierTest extends ClassifierTest {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
index 387c0da..a8cce00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertThat;
 
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 
 import androidx.test.filters.SmallTest;
 
@@ -35,7 +34,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class ZigZagClassifierTest extends ClassifierTest {
 
     private FalsingClassifier mClassifier;
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 48169ea..0a3bc6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -154,12 +154,14 @@
 
     @Test
     public void onAlignmentStateChanged_showsSlowChargingIndication() {
-        createController();
-        verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
-        mController.setVisible(true);
+        mInstrumentation.runOnMainSync(() -> {
+            createController();
+            verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+            mController.setVisible(true);
 
-        mAlignmentListener.getValue().onAlignmentStateChanged(
-                DockManager.ALIGN_STATE_POOR);
+            mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR);
+        });
+        mInstrumentation.waitForIdleSync();
 
         assertThat(mTextView.getText()).isEqualTo(
                 mContext.getResources().getString(R.string.dock_alignment_slow_charging));
@@ -169,11 +171,14 @@
 
     @Test
     public void onAlignmentStateChanged_showsNotChargingIndication() {
-        createController();
-        verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
-        mController.setVisible(true);
+        mInstrumentation.runOnMainSync(() -> {
+            createController();
+            verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+            mController.setVisible(true);
 
-        mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+            mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+        });
+        mInstrumentation.waitForIdleSync();
 
         assertThat(mTextView.getText()).isEqualTo(
                 mContext.getResources().getString(R.string.dock_alignment_not_charging));
@@ -183,13 +188,15 @@
 
     @Test
     public void onAlignmentStateChanged_whileDozing_showsSlowChargingIndication() {
-        createController();
-        verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
-        mController.setVisible(true);
-        mController.setDozing(true);
+        mInstrumentation.runOnMainSync(() -> {
+            createController();
+            verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+            mController.setVisible(true);
+            mController.setDozing(true);
 
-        mAlignmentListener.getValue().onAlignmentStateChanged(
-                DockManager.ALIGN_STATE_POOR);
+            mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_POOR);
+        });
+        mInstrumentation.waitForIdleSync();
 
         assertThat(mTextView.getText()).isEqualTo(
                 mContext.getResources().getString(R.string.dock_alignment_slow_charging));
@@ -199,12 +206,15 @@
 
     @Test
     public void onAlignmentStateChanged_whileDozing_showsNotChargingIndication() {
-        createController();
-        verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
-        mController.setVisible(true);
-        mController.setDozing(true);
+        mInstrumentation.runOnMainSync(() -> {
+            createController();
+            verify(mDockManager).addAlignmentStateListener(mAlignmentListener.capture());
+            mController.setVisible(true);
+            mController.setDozing(true);
 
-        mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+            mAlignmentListener.getValue().onAlignmentStateChanged(DockManager.ALIGN_STATE_TERRIBLE);
+        });
+        mInstrumentation.waitForIdleSync();
 
         assertThat(mTextView.getText()).isEqualTo(
                 mContext.getResources().getString(R.string.dock_alignment_not_charging));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index d580234..afbe668 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -36,7 +36,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationListener.NotifServiceListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -51,7 +51,7 @@
     private static final String TEST_PACKAGE_NAME = "test";
     private static final int TEST_UID = 0;
 
-    @Mock private NotifServiceListener mServiceListener;
+    @Mock private NotificationHandler mNotificationHandler;
     @Mock private NotificationManager mNotificationManager;
 
     private NotificationListener mListener;
@@ -69,21 +69,21 @@
         mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
                 new Notification(), UserHandle.CURRENT, null, 0);
 
-        mListener.addNotificationListener(mServiceListener);
+        mListener.addNotificationHandler(mNotificationHandler);
     }
 
     @Test
     public void testNotificationAddCallsAddNotification() {
         mListener.onNotificationPosted(mSbn, mRanking);
         TestableLooper.get(this).processAllMessages();
-        verify(mServiceListener).onNotificationPosted(mSbn, mRanking);
+        verify(mNotificationHandler).onNotificationPosted(mSbn, mRanking);
     }
 
     @Test
     public void testNotificationRemovalCallsRemoveNotification() {
         mListener.onNotificationRemoved(mSbn, mRanking);
         TestableLooper.get(this).processAllMessages();
-        verify(mServiceListener).onNotificationRemoved(eq(mSbn), eq(mRanking), anyInt());
+        verify(mNotificationHandler).onNotificationRemoved(eq(mSbn), eq(mRanking), anyInt());
     }
 
     @Test
@@ -91,7 +91,7 @@
         mListener.onNotificationRankingUpdate(mRanking);
         TestableLooper.get(this).processAllMessages();
         // RankingMap may be modified by plugins.
-        verify(mServiceListener).onNotificationRankingUpdate(any());
+        verify(mNotificationHandler).onNotificationRankingUpdate(any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
index 94b3ac4..2605402 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SbnBuilder.java
@@ -116,6 +116,27 @@
 
     public SbnBuilder setNotification(Notification notification) {
         mNotification = notification;
+        mNotificationBuilder = null;
+        return this;
+    }
+
+    public SbnBuilder setContentTitle(Context context, String contentTitle) {
+        modifyNotification(context).setContentTitle(contentTitle);
+        return this;
+    }
+
+    public SbnBuilder setContentText(Context context, String contentText) {
+        modifyNotification(context).setContentText(contentText);
+        return this;
+    }
+
+    public SbnBuilder setGroup(Context context, String groupKey) {
+        modifyNotification(context).setGroup(groupKey);
+        return this;
+    }
+
+    public SbnBuilder setGroupSummary(Context context, boolean isGroupSummary) {
+        modifyNotification(context).setGroupSummary(isGroupSummary);
         return this;
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
index 145a25c..a54f733 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimatorTest.java
@@ -33,7 +33,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
 
@@ -64,7 +64,7 @@
         mLaunchAnimator = new ActivityLaunchAnimator(
                 mStatusBarWindowViewController,
                 mCallback,
-                mock(NotificationPanelView.class),
+                mock(NotificationPanelViewController.class),
                 mNotificationContainer);
 
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
new file mode 100644
index 0000000..c113df0b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Simulates a NotificationManager
+ *
+ * You can post and retract notifications, each with an accompanying Ranking. The simulator will
+ * keep its RankingMap up to date and call appropriate event listeners.
+ */
+public class NoManSimulator {
+    private final List<NotificationHandler> mListeners = new ArrayList<>();
+    private final Map<String, Ranking> mRankings = new ArrayMap<>();
+
+    public NoManSimulator() {
+    }
+
+    public void addListener(NotificationHandler listener) {
+        mListeners.add(listener);
+    }
+
+    public NotifEvent postNotif(NotificationEntryBuilder builder) {
+        final NotificationEntry entry = builder.build();
+        mRankings.put(entry.getKey(), entry.getRanking());
+        final RankingMap rankingMap = buildRankingMap();
+        for (NotificationHandler listener : mListeners) {
+            listener.onNotificationPosted(entry.getSbn(), rankingMap);
+        }
+        return new NotifEvent(entry.getSbn(), entry.getRanking(), rankingMap);
+    }
+
+    public NotifEvent retractNotif(StatusBarNotification sbn, int reason) {
+        assertNotNull(mRankings.remove(sbn.getKey()));
+        final RankingMap rankingMap = buildRankingMap();
+        for (NotificationHandler listener : mListeners) {
+            listener.onNotificationRemoved(sbn, rankingMap, reason);
+        }
+        return new NotifEvent(sbn, null, rankingMap);
+    }
+
+    public void issueRankingUpdate() {
+        final RankingMap rankingMap = buildRankingMap();
+        for (NotificationHandler listener : mListeners) {
+            listener.onNotificationRankingUpdate(rankingMap);
+        }
+    }
+
+    public void setRanking(String key, Ranking ranking) {
+        mRankings.put(key, ranking);
+    }
+
+    private RankingMap buildRankingMap() {
+        return new RankingMap(mRankings.values().toArray(new Ranking[0]));
+    }
+
+    public static class NotifEvent {
+        public final String key;
+        public final StatusBarNotification sbn;
+        public final Ranking ranking;
+        public final RankingMap rankingMap;
+
+        private NotifEvent(
+                StatusBarNotification sbn,
+                Ranking ranking,
+                RankingMap rankingMap) {
+            this.key = sbn.getKey();
+            this.sbn = sbn;
+            this.ranking = ranking;
+            this.rankingMap = rankingMap;
+        }
+    }
+}
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 0dcd253..0837a42 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
@@ -19,39 +19,41 @@
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_CLICK;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationListener.NotifServiceListener;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
 import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
+import com.android.systemui.statusbar.notification.collection.notifcollection.CoalescedEvent;
+import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer;
+import com.android.systemui.statusbar.notification.collection.notifcollection.GroupCoalescer.BatchableNotificationHandler;
 import com.android.systemui.util.Assert;
 
 import org.junit.Before;
@@ -64,6 +66,8 @@
 import org.mockito.Spy;
 
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 @SmallTest
@@ -72,18 +76,20 @@
 public class NotifCollectionTest extends SysuiTestCase {
 
     @Mock private IStatusBarService mStatusBarService;
-    @Mock private NotificationListener mListenerService;
+    @Mock private GroupCoalescer mGroupCoalescer;
     @Spy private RecordingCollectionListener mCollectionListener;
+    @Mock private CollectionReadyForBuildListener mBuildListener;
 
     @Spy private RecordingLifetimeExtender mExtender1 = new RecordingLifetimeExtender("Extender1");
     @Spy private RecordingLifetimeExtender mExtender2 = new RecordingLifetimeExtender("Extender2");
     @Spy private RecordingLifetimeExtender mExtender3 = new RecordingLifetimeExtender("Extender3");
 
-    @Captor private ArgumentCaptor<NotifServiceListener> mListenerCaptor;
+    @Captor private ArgumentCaptor<BatchableNotificationHandler> mListenerCaptor;
     @Captor private ArgumentCaptor<NotificationEntry> mEntryCaptor;
+    @Captor private ArgumentCaptor<Collection<NotificationEntry>> mBuildListCaptor;
 
     private NotifCollection mCollection;
-    private NotifServiceListener mServiceListener;
+    private BatchableNotificationHandler mNotifHandler;
 
     private NoManSimulator mNoMan;
 
@@ -93,21 +99,23 @@
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
 
         mCollection = new NotifCollection(mStatusBarService);
-        mCollection.attach(mListenerService);
+        mCollection.attach(mGroupCoalescer);
         mCollection.addCollectionListener(mCollectionListener);
+        mCollection.setBuildListener(mBuildListener);
 
         // Capture the listener object that the collection registers with the listener service so
         // we can simulate listener service events in tests below
-        verify(mListenerService).addNotificationListener(mListenerCaptor.capture());
-        mServiceListener = checkNotNull(mListenerCaptor.getValue());
+        verify(mGroupCoalescer).setNotificationHandler(mListenerCaptor.capture());
+        mNotifHandler = requireNonNull(mListenerCaptor.getValue());
 
-        mNoMan = new NoManSimulator(mServiceListener);
+        mNoMan = new NoManSimulator();
+        mNoMan.addListener(mNotifHandler);
     }
 
     @Test
     public void testEventDispatchedWhenNotifPosted() {
         // WHEN a notification is posted
-        PostedNotif notif1 = mNoMan.postNotif(
+        NotifEvent notif1 = mNoMan.postNotif(
                 buildNotif(TEST_PACKAGE, 3)
                         .setRank(4747));
 
@@ -121,13 +129,68 @@
     }
 
     @Test
+    public void testEventDispatchedWhenNotifBatchPosted() {
+        // GIVEN a NotifCollection with one notif already posted
+        mNoMan.postNotif(buildNotif(TEST_PACKAGE, 2)
+                .setGroup(mContext, "group_1")
+                .setContentTitle(mContext, "Old version"));
+
+        clearInvocations(mCollectionListener);
+        clearInvocations(mBuildListener);
+
+        // WHEN three notifications from the same group are posted (one of them an update, two of
+        // them new)
+        NotificationEntry entry1 = buildNotif(TEST_PACKAGE, 1)
+                .setGroup(mContext, "group_1")
+                .build();
+        NotificationEntry entry2 = buildNotif(TEST_PACKAGE, 2)
+                .setGroup(mContext, "group_1")
+                .setContentTitle(mContext, "New version")
+                .build();
+        NotificationEntry entry3 = buildNotif(TEST_PACKAGE, 3)
+                .setGroup(mContext, "group_1")
+                .build();
+
+        mNotifHandler.onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(entry1.getKey(), 0, entry1.getSbn(), entry1.getRanking(), null),
+                new CoalescedEvent(entry2.getKey(), 1, entry2.getSbn(), entry2.getRanking(), null),
+                new CoalescedEvent(entry3.getKey(), 2, entry3.getSbn(), entry3.getRanking(), null)
+        ));
+
+        // THEN onEntryAdded is called on the new ones
+        verify(mCollectionListener, times(2)).onEntryAdded(mEntryCaptor.capture());
+
+        List<NotificationEntry> capturedAdds = mEntryCaptor.getAllValues();
+
+        assertEquals(entry1.getSbn(), capturedAdds.get(0).getSbn());
+        assertEquals(entry1.getRanking(), capturedAdds.get(0).getRanking());
+
+        assertEquals(entry3.getSbn(), capturedAdds.get(1).getSbn());
+        assertEquals(entry3.getRanking(), capturedAdds.get(1).getRanking());
+
+        // THEN onEntryUpdated is called on the middle one
+        verify(mCollectionListener).onEntryUpdated(mEntryCaptor.capture());
+        NotificationEntry capturedUpdate = mEntryCaptor.getValue();
+        assertEquals(entry2.getSbn(), capturedUpdate.getSbn());
+        assertEquals(entry2.getRanking(), capturedUpdate.getRanking());
+
+        // THEN onBuildList is called only once
+        verify(mBuildListener).onBuildList(mBuildListCaptor.capture());
+        assertEquals(new ArraySet<>(Arrays.asList(
+                capturedAdds.get(0),
+                capturedAdds.get(1),
+                capturedUpdate
+        )), new ArraySet<>(mBuildListCaptor.getValue()));
+    }
+
+    @Test
     public void testEventDispatchedWhenNotifUpdated() {
         // GIVEN a collection with one notif
         mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
                 .setRank(4747));
 
         // WHEN the notif is reposted
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
                 .setRank(89));
 
         // THEN the listener is notified
@@ -145,7 +208,7 @@
         mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
         clearInvocations(mCollectionListener);
 
-        PostedNotif notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
         NotificationEntry entry = mCollectionListener.getEntry(notif.key);
         clearInvocations(mCollectionListener);
 
@@ -161,7 +224,7 @@
     @Test
     public void testRankingsAreUpdatedForOtherNotifs() {
         // GIVEN a collection with one notif
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
                 .setRank(47));
         NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
 
@@ -178,11 +241,11 @@
     @Test
     public void testRankingUpdateIsProperlyIssuedToEveryone() {
         // GIVEN a collection with a couple notifs
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3)
                 .setRank(3));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 8)
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 8)
                 .setRank(2));
-        PostedNotif notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 77)
+        NotifEvent notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 77)
                 .setRank(1));
 
         NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
@@ -217,7 +280,7 @@
     @Test
     public void testNotifEntriesAreNotPersistedAcrossRemovalAndReposting() {
         // GIVEN a notification that has been posted
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 3));
         NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
 
         // WHEN the notification is retracted and then reposted
@@ -234,8 +297,8 @@
         // GIVEN a collection with a couple notifications and a lifetime extender
         mCollection.addNotificationLifetimeExtender(mExtender1);
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88, "barTag"));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47, "myTag"));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88, "barTag"));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // WHEN a notification is manually dismissed
@@ -267,9 +330,9 @@
     @Test(expected = IllegalStateException.class)
     public void testDismissingNonExistentNotificationThrows() {
         // GIVEN a collection that originally had three notifs, but where one was dismissed
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
-        PostedNotif notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 99));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif3 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 99));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
         mNoMan.retractNotif(notif2.sbn, REASON_UNKNOWN);
 
@@ -292,8 +355,8 @@
         mCollection.addNotificationLifetimeExtender(mExtender2);
         mCollection.addNotificationLifetimeExtender(mExtender3);
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // WHEN a notification is removed
@@ -320,8 +383,8 @@
         mCollection.addNotificationLifetimeExtender(mExtender2);
         mCollection.addNotificationLifetimeExtender(mExtender3);
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // GIVEN a notification gets lifetime-extended by one of them
@@ -357,8 +420,8 @@
         mCollection.addNotificationLifetimeExtender(mExtender2);
         mCollection.addNotificationLifetimeExtender(mExtender3);
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // GIVEN a notification gets lifetime-extended by a couple of them
@@ -392,8 +455,8 @@
         mCollection.addNotificationLifetimeExtender(mExtender2);
         mCollection.addNotificationLifetimeExtender(mExtender3);
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // GIVEN a notification gets lifetime-extended by a couple of them
@@ -422,8 +485,8 @@
         mExtender1.shouldExtendLifetime = true;
         mExtender2.shouldExtendLifetime = true;
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // GIVEN a notification gets lifetime-extended by a couple of them
@@ -452,8 +515,8 @@
         mExtender1.shouldExtendLifetime = true;
         mExtender2.shouldExtendLifetime = true;
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // GIVEN a notification gets lifetime-extended by a couple of them
@@ -481,8 +544,8 @@
         mExtender1.shouldExtendLifetime = true;
         mExtender2.shouldExtendLifetime = true;
 
-        PostedNotif notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
-        PostedNotif notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
+        NotifEvent notif1 = mNoMan.postNotif(buildNotif(TEST_PACKAGE, 47));
+        NotifEvent notif2 = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88));
         NotificationEntry entry2 = mCollectionListener.getEntry(notif2.key);
 
         // GIVEN a notification gets lifetime-extended by a couple of them
@@ -491,7 +554,7 @@
         clearInvocations(mExtender1, mExtender2, mExtender3);
 
         // WHEN the notification is reposted
-        PostedNotif notif2a = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88)
+        NotifEvent notif2a = mNoMan.postNotif(buildNotif(TEST_PACKAGE2, 88)
                 .setRank(4747)
                 .setExplanation("Some new explanation"));
 
@@ -512,53 +575,6 @@
                 .setId(id);
     }
 
-    private static class NoManSimulator {
-        private final NotifServiceListener mListener;
-        private final Map<String, Ranking> mRankings = new ArrayMap<>();
-
-        private NoManSimulator(
-                NotifServiceListener listener) {
-            mListener = listener;
-        }
-
-        PostedNotif postNotif(NotificationEntryBuilder builder) {
-            NotificationEntry entry = builder.build();
-            mRankings.put(entry.getKey(), entry.getRanking());
-            mListener.onNotificationPosted(entry.getSbn(), buildRankingMap());
-            return new PostedNotif(entry.getSbn(), entry.getRanking());
-        }
-
-        void retractNotif(StatusBarNotification sbn, int reason) {
-            assertNotNull(mRankings.remove(sbn.getKey()));
-            mListener.onNotificationRemoved(sbn, buildRankingMap(), reason);
-        }
-
-        void issueRankingUpdate() {
-            mListener.onNotificationRankingUpdate(buildRankingMap());
-        }
-
-        void setRanking(String key, Ranking ranking) {
-            mRankings.put(key, ranking);
-        }
-
-        private RankingMap buildRankingMap() {
-            return new RankingMap(mRankings.values().toArray(new Ranking[0]));
-        }
-    }
-
-    private static class PostedNotif {
-        public final String key;
-        public final StatusBarNotification sbn;
-        public final Ranking ranking;
-
-        private PostedNotif(StatusBarNotification sbn,
-                Ranking ranking) {
-            this.key = sbn.getKey();
-            this.sbn = sbn;
-            this.ranking = ranking;
-        }
-    }
-
     private static class RecordingCollectionListener implements NotifCollectionListener {
         private final Map<String, NotificationEntry> mLastSeenEntries = new ArrayMap<>();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
index 6e9c2c8..3e4068b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifListBuilderImplTest.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.systemui.statusbar.notification.collection.ListDumper.dumpList;
 
 import static org.junit.Assert.assertEquals;
@@ -67,6 +66,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 @SmallTest
@@ -105,7 +105,7 @@
         mListBuilder.attach(mNotifCollection);
 
         Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
-        mReadyForBuildListener = checkNotNull(mBuildListenerCaptor.getValue());
+        mReadyForBuildListener = Objects.requireNonNull(mBuildListenerCaptor.getValue());
     }
 
     @Test
@@ -1021,7 +1021,6 @@
             mPendingSet.clear();
         }
 
-        mReadyForBuildListener.onBeginDispatchToListeners();
         mReadyForBuildListener.onBuildList(mEntrySet);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
index e6a61d6..300ec18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilder.java
@@ -140,6 +140,28 @@
         return this;
     }
 
+    /* Delegated to Notification.Builder (via SbnBuilder) */
+
+    public NotificationEntryBuilder setContentTitle(Context context, String contentTitle) {
+        mSbnBuilder.setContentTitle(context, contentTitle);
+        return this;
+    }
+
+    public NotificationEntryBuilder setContentText(Context context, String contentText) {
+        mSbnBuilder.setContentText(context, contentText);
+        return this;
+    }
+
+    public NotificationEntryBuilder setGroup(Context context, String groupKey) {
+        mSbnBuilder.setGroup(context, groupKey);
+        return this;
+    }
+
+    public NotificationEntryBuilder setGroupSummary(Context context, boolean isGroupSummary) {
+        mSbnBuilder.setGroupSummary(context, isGroupSummary);
+        return this;
+    }
+
     /* Delegated to RankingBuilder */
 
     public NotificationEntryBuilder setRank(int rank) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescerTest.java
new file mode 100644
index 0000000..7ff3240
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/GroupCoalescerTest.java
@@ -0,0 +1,323 @@
+/*
+ * 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.collection.notifcollection;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.NoManSimulator;
+import com.android.systemui.statusbar.notification.collection.NoManSimulator.NotifEvent;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.logging.NotifLog;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class GroupCoalescerTest extends SysuiTestCase {
+
+    private GroupCoalescer mCoalescer;
+
+    @Mock private NotificationListener mListenerService;
+    @Mock private GroupCoalescer.BatchableNotificationHandler mListener;
+    @Mock private NotifLog mLog;
+
+    @Captor private ArgumentCaptor<NotificationHandler> mListenerCaptor;
+
+    private final NoManSimulator mNoMan = new NoManSimulator();
+    private final FakeSystemClock mClock = new FakeSystemClock();
+    private final FakeExecutor mExecutor = new FakeExecutor(mClock);
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mCoalescer =
+                new GroupCoalescer(
+                        mExecutor,
+                        mClock,
+                        mLog,
+                        LINGER_DURATION);
+        mCoalescer.setNotificationHandler(mListener);
+        mCoalescer.attach(mListenerService);
+
+        verify(mListenerService).addNotificationHandler(mListenerCaptor.capture());
+        NotificationHandler serviceListener = checkNotNull(mListenerCaptor.getValue());
+        mNoMan.addListener(serviceListener);
+    }
+
+    @Test
+    public void testUngroupedNotificationsAreNotCoalesced() {
+        // WHEN a notification that doesn't have a group key is posted
+        NotifEvent notif1 = mNoMan.postNotif(
+                new NotificationEntryBuilder()
+                        .setId(0)
+                        .setPkg(TEST_PACKAGE_A));
+        mClock.advanceTime(LINGER_DURATION);
+
+        // THEN the event is passed through to the handler
+        verify(mListener).onNotificationPosted(notif1.sbn, notif1.rankingMap);
+
+        // Then the event isn't emitted in a batch
+        verify(mListener, never()).onNotificationBatchPosted(anyList());
+    }
+
+    @Test
+    public void testGroupedNotificationsAreCoalesced() {
+        // WHEN a notification that has a group key is posted
+        NotifEvent notif1 = mNoMan.postNotif(
+                new NotificationEntryBuilder()
+                        .setId(0)
+                        .setPkg(TEST_PACKAGE_A)
+                        .setGroup(mContext, GROUP_1));
+
+        // THEN the event is not passed on to the handler
+        verify(mListener, never()).onNotificationPosted(
+                any(StatusBarNotification.class),
+                any(RankingMap.class));
+
+        // Then the event isn't (yet) emitted in a batch
+        verify(mListener, never()).onNotificationBatchPosted(anyList());
+    }
+
+    @Test
+    public void testCoalescedNotificationsStillPassThroughRankingUpdate() {
+        // WHEN a notification that has a group key is posted
+        NotifEvent notif1 = mNoMan.postNotif(
+                new NotificationEntryBuilder()
+                        .setId(0)
+                        .setPkg(TEST_PACKAGE_A)
+                        .setGroup(mContext, GROUP_1));
+
+        // THEN the listener receives a ranking update instead of an add
+        verify(mListener).onNotificationRankingUpdate(notif1.rankingMap);
+    }
+
+    @Test
+    public void testCoalescedNotificationsArePosted() {
+        // GIVEN three notifs are posted that are part of the same group
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setGroup(mContext, GROUP_1));
+
+        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setGroup(mContext, GROUP_1)
+                .setGroupSummary(mContext, true));
+
+        NotifEvent notif3 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(3)
+                .setGroup(mContext, GROUP_1));
+
+        verify(mListener, never()).onNotificationPosted(
+                any(StatusBarNotification.class),
+                any(RankingMap.class));
+        verify(mListener, never()).onNotificationBatchPosted(anyList());
+
+        // WHEN enough time passes
+        mClock.advanceTime(LINGER_DURATION);
+
+        // THEN the coalesced notifs are applied. The summary is sorted to the front.
+        verify(mListener).onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(notif2.key, 1, notif2.sbn, notif2.ranking, null),
+                new CoalescedEvent(notif1.key, 0, notif1.sbn, notif1.ranking, null),
+                new CoalescedEvent(notif3.key, 2, notif3.sbn, notif3.ranking, null)
+        ));
+    }
+
+    @Test
+    public void testCoalescedEventsThatAreLaterUngroupedAreEmittedImmediatelyAndNotLater() {
+        // GIVEN a few newly posted notifications in the same group
+        NotifEvent notif1a = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setContentTitle(mContext, "Grouped message")
+                .setGroup(mContext, GROUP_1));
+        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setGroup(mContext, GROUP_1));
+        NotifEvent notif3 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(3)
+                .setGroup(mContext, GROUP_1));
+
+        verify(mListener, never()).onNotificationPosted(
+                any(StatusBarNotification.class),
+                any(RankingMap.class));
+        verify(mListener, never()).onNotificationBatchPosted(anyList());
+
+        // WHEN one of them is updated to no longer be in the group
+        NotifEvent notif1b = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setContentTitle(mContext, "Oops no longer grouped"));
+
+        // THEN the pre-existing batch is first emitted
+        InOrder inOrder = inOrder(mListener);
+        inOrder.verify(mListener).onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(notif1a.key, 0, notif1a.sbn, notif1a.ranking, null),
+                new CoalescedEvent(notif2.key, 1, notif2.sbn, notif2.ranking, null),
+                new CoalescedEvent(notif3.key, 2, notif3.sbn, notif3.ranking, null)
+        ));
+
+        // THEN the updated notif is emitted
+        inOrder.verify(mListener).onNotificationPosted(notif1b.sbn, notif1b.rankingMap);
+
+        // WHEN the time runs out on the remainder of the group
+        clearInvocations(mListener);
+        mClock.advanceTime(LINGER_DURATION);
+
+        // THEN no lingering batch is applied
+        verify(mListener, never()).onNotificationBatchPosted(anyList());
+    }
+
+    @Test
+    public void testUpdatingCoalescedNotifTriggersBatchEmit() {
+        // GIVEN two grouped, coalesced notifications
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setGroup(mContext, GROUP_1));
+        NotifEvent notif2a = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setContentTitle(mContext, "Version 1")
+                .setGroup(mContext, GROUP_1));
+
+        // WHEN one of them gets updated
+        NotifEvent notif2b = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setContentTitle(mContext, "Version 2")
+                .setGroup(mContext, GROUP_1));
+
+        // THEN first, the coalesced group is emitted
+        verify(mListener).onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(notif1.key, 0, notif1.sbn, notif1.ranking, null),
+                new CoalescedEvent(notif2a.key, 1, notif2a.sbn, notif2a.ranking, null)
+        ));
+        verify(mListener, never()).onNotificationPosted(
+                any(StatusBarNotification.class),
+                any(RankingMap.class));
+
+        // THEN second, the update is emitted
+        mClock.advanceTime(LINGER_DURATION);
+        verify(mListener).onNotificationBatchPosted(Collections.singletonList(
+                new CoalescedEvent(notif2b.key, 0, notif2b.sbn, notif2b.ranking, null)
+        ));
+    }
+
+    @Test
+    public void testRemovingCoalescedNotifTriggersBatchEmit() {
+        // GIVEN two grouped, coalesced notifications
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setGroup(mContext, GROUP_1));
+        NotifEvent notif2a = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setGroup(mContext, GROUP_1));
+
+        // WHEN one of them gets retracted
+        NotifEvent notif2b = mNoMan.retractNotif(notif2a.sbn, 0);
+
+        // THEN first, the coalesced group is emitted
+        InOrder inOrder = inOrder(mListener);
+        inOrder.verify(mListener).onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(notif1.key, 0, notif1.sbn, notif1.ranking, null),
+                new CoalescedEvent(notif2a.key, 1, notif2a.sbn, notif2a.ranking, null)
+        ));
+
+        // THEN second, the removal is emitted
+        inOrder.verify(mListener).onNotificationRemoved(notif2b.sbn, notif2b.rankingMap, 0);
+    }
+
+    @Test
+    public void testRankingsAreUpdated() {
+        // GIVEN a couple coalesced notifications
+        NotifEvent notif1 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(1)
+                .setGroup(mContext, GROUP_1));
+        NotifEvent notif2 = mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_A)
+                .setId(2)
+                .setGroup(mContext, GROUP_1));
+
+        // WHEN an update to an unrelated notification comes in that updates their rankings
+        Ranking ranking1b = new RankingBuilder()
+                .setKey(notif1.key)
+                .setLastAudiblyAlertedMs(4747)
+                .build();
+        Ranking ranking2b = new RankingBuilder()
+                .setKey(notif2.key)
+                .setLastAudiblyAlertedMs(3333)
+                .build();
+        mNoMan.setRanking(notif1.key, ranking1b);
+        mNoMan.setRanking(notif2.key, ranking2b);
+        mNoMan.postNotif(new NotificationEntryBuilder()
+                .setPkg(TEST_PACKAGE_B)
+                .setId(17));
+
+        // THEN they have the new rankings when they are eventually emitted
+        mClock.advanceTime(LINGER_DURATION);
+        verify(mListener).onNotificationBatchPosted(Arrays.asList(
+                new CoalescedEvent(notif1.key, 0, notif1.sbn, ranking1b, null),
+                new CoalescedEvent(notif2.key, 1, notif2.sbn, ranking2b, null)
+        ));
+    }
+
+    private static final long LINGER_DURATION = 4700;
+
+    private static final String TEST_PACKAGE_A = "com.test.package_a";
+    private static final String TEST_PACKAGE_B = "com.test.package_b";
+    private static final String GROUP_1 = "group_1";
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 8decae3..ae87eef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -64,7 +64,8 @@
         mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
         mNotificationAreaInner = mock(View.class);
         mCenteredNotificationAreaView = mock(View.class);
-        when(statusBar.getPanel()).thenReturn(mock(NotificationPanelView.class));
+        when(statusBar.getPanelController()).thenReturn(
+                mock(NotificationPanelViewController.class));
         when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class));
         when(mMockNotificiationAreaController.getNotificationInnerAreaView()).thenReturn(
                 mNotificationAreaInner);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 46f6cfe..d31f175 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -86,7 +86,7 @@
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private StatusBarWindowViewController mStatusBarWindowViewController;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    @Mock private NotificationPanelView mNotificationPanel;
+    @Mock private NotificationPanelViewController mNotificationPanel;
     @Mock private View mAmbientIndicationContainer;
     @Mock private BiometricUnlockController mBiometricUnlockController;
     @Mock private LockscreenLockIconController mLockscreenLockIconController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
index 0260269..7448dbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java
@@ -53,7 +53,8 @@
 
     private final NotificationStackScrollLayout mStackScroller =
             mock(NotificationStackScrollLayout.class);
-    private final NotificationPanelView mPanelView = mock(NotificationPanelView.class);
+    private final NotificationPanelViewController mPanelView =
+            mock(NotificationPanelViewController.class);
     private final DarkIconDispatcher mDarkIconDispatcher = mock(DarkIconDispatcher.class);
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
     private ExpandableNotificationRow mFirst;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
index a024454..3d59d61 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LightsOutNotifControllerTest.java
@@ -18,8 +18,6 @@
 
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
@@ -50,6 +48,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Objects;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -85,11 +85,11 @@
 
         // Capture the entry listener object so we can simulate events in tests below
         verify(mEntryManager).addNotificationEntryListener(mListenerCaptor.capture());
-        mEntryListener = checkNotNull(mListenerCaptor.getValue());
+        mEntryListener = Objects.requireNonNull(mListenerCaptor.getValue());
 
         // Capture the callback object so we can simulate callback events in tests below
         verify(mCommandQueue).addCallback(mCallbacksCaptor.capture());
-        mCallbacks = checkNotNull(mCallbacksCaptor.getValue());
+        mCallbacks = Objects.requireNonNull(mCallbacksCaptor.getValue());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c165e56..1f37ad8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -16,9 +16,13 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -26,37 +30,44 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
 import android.app.StatusBarManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.hardware.biometrics.BiometricSourceType;
+import android.os.PowerManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.util.DisplayMetrics;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.keyguard.KeyguardStatusView;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SystemUIFactory;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.StatusBarStateControllerImpl;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
-import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -70,6 +81,7 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
 import java.util.function.Consumer;
 
@@ -85,8 +97,6 @@
     @Mock
     private NotificationStackScrollLayout mNotificationStackScrollLayout;
     @Mock
-    private KeyguardStatusView mKeyguardStatusView;
-    @Mock
     private KeyguardBottomAreaView mKeyguardBottomArea;
     @Mock
     private KeyguardBottomAreaView mQsFrame;
@@ -109,27 +119,89 @@
     @Mock
     private PanelBar mPanelBar;
     @Mock
-    private KeyguardAffordanceHelper mAffordanceHelper;
-    @Mock
     private KeyguardUpdateMonitor mUpdateMonitor;
     @Mock
     private FalsingManager mFalsingManager;
     @Mock
     private KeyguardBypassController mKeyguardBypassController;
-    @Mock private DozeParameters mDozeParameters;
-    private NotificationPanelView mNotificationPanelView;
+    @Mock
+    private DozeParameters mDozeParameters;
+    @Mock
+    private NotificationPanelView mView;
+    @Mock
+    private InjectionInflationController mInjectionInflationController;
+    @Mock
+    private DynamicPrivacyController mDynamicPrivacyController;
+    @Mock
+    private PluginManager mPluginManager;
+    @Mock
+    private ShadeController mShadeController;
+    @Mock
+    private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
+    @Mock
+    private NotificationEntryManager mNotificationEntryManager;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private DozeLog mDozeLog;
+    @Mock
+    private CommandQueue mCommandQueue;
+    @Mock
+    private VibratorHelper mVibratorHelper;
+    @Mock
+    private LatencyTracker mLatencyTracker;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private MetricsLogger mMetricsLogger;
+    @Mock
+    private ActivityManager mActivityManager;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private Configuration mConfiguration;
+    private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    @Mock
+    private KeyguardClockSwitch mKeyguardClockSwitch;
+    private PanelViewController.TouchHandler mTouchHandler;
+    @Mock
+    private ZenModeController mZenModeController;
+    @Mock
+    private ConfigurationController mConfigurationController;
+    private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+
+    private NotificationPanelViewController mNotificationPanelViewController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        when(mHeadsUpCallback.getContext()).thenReturn(mContext);
+        when(mView.getResources()).thenReturn(mResources);
+        when(mResources.getConfiguration()).thenReturn(mConfiguration);
+        mConfiguration.orientation = ORIENTATION_PORTRAIT;
+        when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
+        mDisplayMetrics.density = 100;
+        when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
+        when(mView.getContext()).thenReturn(getContext());
+        when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch);
+        when(mView.findViewById(R.id.notification_stack_scroller))
+                .thenReturn(mNotificationStackScrollLayout);
         when(mNotificationStackScrollLayout.getHeight()).thenReturn(1000);
         when(mNotificationStackScrollLayout.getHeadsUpCallback()).thenReturn(mHeadsUpCallback);
-        when(mHeadsUpCallback.getContext()).thenReturn(mContext);
-        mDependency.injectTestDependency(StatusBarStateController.class,
-                mStatusBarStateController);
-        mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mUpdateMonitor);
-        mDependency.injectMockDependency(ConfigurationController.class);
-        mDependency.injectMockDependency(ZenModeController.class);
+        when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea);
+        when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
+        when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
+        when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
+        when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
+        mFlingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(mDisplayMetrics);
+
+        doAnswer((Answer<Void>) invocation -> {
+            mTouchHandler = invocation.getArgument(0);
+            return null;
+        }).when(mView).setOnTouchListener(any(PanelViewController.TouchHandler.class));
+
         NotificationWakeUpCoordinator coordinator =
                 new NotificationWakeUpCoordinator(
                         mock(HeadsUpManagerPhone.class),
@@ -143,18 +215,26 @@
                 mock(NotificationRoundnessManager.class),
                 mStatusBarStateController,
                 new FalsingManagerFake());
-        mNotificationPanelView = new TestableNotificationPanelView(coordinator, expansionHandler,
-                mKeyguardBypassController, mStatusBarStateController);
-        mNotificationPanelView.setHeadsUpManager(mHeadsUpManager);
-        mNotificationPanelView.setBar(mPanelBar);
-
-        when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
-        when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
+        mNotificationPanelViewController = new NotificationPanelViewController(mView,
+                mInjectionInflationController,
+                coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
+                mFalsingManager, mPluginManager, mShadeController,
+                mNotificationLockscreenUserManager, mNotificationEntryManager,
+                mKeyguardStateController, mStatusBarStateController, mDozeLog,
+                mDozeParameters, mCommandQueue, mVibratorHelper,
+                mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
+                mMetricsLogger, mActivityManager, mZenModeController, mConfigurationController,
+                mFlingAnimationUtilsBuilder);
+        mNotificationPanelViewController.initDependencies(mStatusBar, mGroupManager,
+                mNotificationShelf, mNotificationAreaController, mScrimController);
+        mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
+        mNotificationPanelViewController.setBar(mPanelBar);
     }
 
     @Test
     public void testSetDozing_notifiesNsslAndStateController() {
-        mNotificationPanelView.setDozing(true /* dozing */, true /* animate */, null /* touch */);
+        mNotificationPanelViewController.setDozing(true /* dozing */, true /* animate */,
+                null /* touch */);
         InOrder inOrder = inOrder(mNotificationStackScrollLayout, mStatusBarStateController);
         inOrder.verify(mNotificationStackScrollLayout).setDozing(eq(true), eq(true), eq(null));
         inOrder.verify(mStatusBarStateController).setDozeAmount(eq(1f), eq(true));
@@ -162,103 +242,63 @@
 
     @Test
     public void testSetExpandedHeight() {
-        mNotificationPanelView.setExpandedHeight(200);
-        assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
+        mNotificationPanelViewController.setExpandedHeight(200);
+        assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
     }
 
     @Test
     public void testAffordanceLaunchingListener() {
         Consumer<Boolean> listener = spy((showing) -> { });
-        mNotificationPanelView.setExpandedFraction(1f);
-        mNotificationPanelView.setLaunchAffordanceListener(listener);
-        mNotificationPanelView.launchCamera(false /* animate */,
+        mNotificationPanelViewController.setExpandedFraction(1f);
+        mNotificationPanelViewController.setLaunchAffordanceListener(listener);
+        mNotificationPanelViewController.launchCamera(false /* animate */,
                 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP);
         verify(listener).accept(eq(true));
 
-        mNotificationPanelView.onAffordanceLaunchEnded();
+        mNotificationPanelViewController.onAffordanceLaunchEnded();
         verify(listener).accept(eq(false));
     }
 
     @Test
     public void testOnTouchEvent_expansionCanBeBlocked() {
-        mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
                 0L /* eventTime */, MotionEvent.ACTION_DOWN, 0f /* x */, 0f /* y */,
                 0 /* metaState */));
-        mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
                 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 200f /* y */,
                 0 /* metaState */));
-        assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
-        assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
+        assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
+        assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
 
-        mNotificationPanelView.blockExpansionForCurrentTouch();
-        mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+        mNotificationPanelViewController.blockExpansionForCurrentTouch();
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
                 0L /* eventTime */, MotionEvent.ACTION_MOVE, 0f /* x */, 300f /* y */,
                 0 /* metaState */));
         // Expansion should not have changed because it was blocked
-        assertThat((int) mNotificationPanelView.getExpandedHeight()).isEqualTo(200);
-        assertThat(mNotificationPanelView.isTrackingBlocked()).isTrue();
+        assertThat((int) mNotificationPanelViewController.getExpandedHeight()).isEqualTo(200);
+        assertThat(mNotificationPanelViewController.isTrackingBlocked()).isTrue();
 
-        mNotificationPanelView.onTouchEvent(MotionEvent.obtain(0L /* downTime */,
+        onTouchEvent(MotionEvent.obtain(0L /* downTime */,
                 0L /* eventTime */, MotionEvent.ACTION_UP, 0f /* x */, 300f /* y */,
                 0 /* metaState */));
-        assertThat(mNotificationPanelView.isTrackingBlocked()).isFalse();
+        assertThat(mNotificationPanelViewController.isTrackingBlocked()).isFalse();
     }
 
     @Test
     public void testKeyguardStatusBarVisibility_hiddenForBypass() {
         when(mUpdateMonitor.shouldListenForFace()).thenReturn(true);
-        mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true,
-                BiometricSourceType.FACE);
+        mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
+                true, BiometricSourceType.FACE);
         verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
 
         when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
-        mNotificationPanelView.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
-        mNotificationPanelView.mKeyguardUpdateCallback.onBiometricRunningStateChanged(true,
-                BiometricSourceType.FACE);
+        mNotificationPanelViewController.mKeyguardUpdateCallback.onFinishedGoingToSleep(0);
+        mNotificationPanelViewController.mKeyguardUpdateCallback.onBiometricRunningStateChanged(
+                true, BiometricSourceType.FACE);
         verify(mKeyguardStatusBar, never()).setVisibility(View.VISIBLE);
     }
 
-    private class TestableNotificationPanelView extends NotificationPanelView {
-        TestableNotificationPanelView(NotificationWakeUpCoordinator coordinator,
-                PulseExpansionHandler expansionHandler,
-                KeyguardBypassController bypassController,
-                SysuiStatusBarStateController statusBarStateController) {
-            super(
-                    NotificationPanelViewTest.this.mContext,
-                    null,
-                    new InjectionInflationController(
-                            SystemUIFactory.getInstance().getRootComponent()),
-                    coordinator,
-                    expansionHandler,
-                    mock(DynamicPrivacyController.class),
-                    bypassController,
-                    mFalsingManager,
-                    mock(PluginManager.class),
-                    mock(ShadeController.class),
-                    mock(NotificationLockscreenUserManager.class),
-                    new NotificationEntryManager(
-                            mock(NotifLog.class),
-                            mock(NotificationGroupManager.class),
-                            mock(NotificationRankingManager.class),
-                            mock(NotificationEntryManager.KeyguardEnvironment.class)),
-                    mock(KeyguardStateController.class),
-                    statusBarStateController,
-                    mock(DozeLog.class),
-                    mDozeParameters,
-                    new CommandQueue(NotificationPanelViewTest.this.mContext));
-            mNotificationStackScroller = mNotificationStackScrollLayout;
-            mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
-            mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar;
-            mKeyguardBottomArea = NotificationPanelViewTest.this.mKeyguardBottomArea;
-            mBigClockContainer = NotificationPanelViewTest.this.mBigClockContainer;
-            mQsFrame = NotificationPanelViewTest.this.mQsFrame;
-            mAffordanceHelper = NotificationPanelViewTest.this.mAffordanceHelper;
-            initDependencies(NotificationPanelViewTest.this.mStatusBar,
-                    NotificationPanelViewTest.this.mGroupManager,
-                    NotificationPanelViewTest.this.mNotificationShelf,
-                    NotificationPanelViewTest.this.mHeadsUpManager,
-                    NotificationPanelViewTest.this.mNotificationAreaController,
-                    NotificationPanelViewTest.this.mScrimController);
-        }
+    private void onTouchEvent(MotionEvent ev) {
+        mTouchHandler.onTouch(mView, ev);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index b27e84a..5b5eaad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -74,7 +74,7 @@
     @Mock
     private ViewGroup mContainer;
     @Mock
-    private NotificationPanelView mNotificationPanelView;
+    private NotificationPanelViewController mNotificationPanelView;
     @Mock
     private BiometricUnlockController mBiometrucUnlockController;
     @Mock
@@ -281,12 +281,12 @@
 
         @Override
         public void registerStatusBar(StatusBar statusBar, ViewGroup container,
-                NotificationPanelView notificationPanelView,
+                NotificationPanelViewController notificationPanelViewController,
                 BiometricUnlockController fingerprintUnlockController,
                 DismissCallbackRegistry dismissCallbackRegistry,
                 ViewGroup lockIconContainer, View notificationContainer,
                 KeyguardBypassController bypassController, FalsingManager falsingManager) {
-            super.registerStatusBar(statusBar, container, notificationPanelView,
+            super.registerStatusBar(statusBar, container, notificationPanelViewController,
                     fingerprintUnlockController, dismissCallbackRegistry, lockIconContainer,
                     notificationContainer, bypassController, falsingManager);
             mBouncer = StatusBarKeyguardViewManagerTest.this.mBouncer;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 86b2a44..fea4b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -64,7 +64,6 @@
 import com.android.systemui.statusbar.NotificationTestHelper;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -124,10 +123,6 @@
     private Intent mContentIntentInner;
     @Mock
     private NotificationActivityStarter mNotificationActivityStarter;
-    @Mock
-    private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
-    @Mock
-    private NotificationPanelView mNotificationPanelView;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
     private NotificationTestHelper mNotificationTestHelper;
@@ -167,8 +162,6 @@
         mActiveNotifications.add(mBubbleNotificationRow.getEntry());
         when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications);
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-        when(mSuperStatusBarViewFactory.getNotificationPanelView())
-                .thenReturn(mNotificationPanelView);
 
         mNotificationActivityStarter = (new StatusBarNotificationActivityStarter.Builder(
                 getContext(), mock(CommandQueue.class), () -> mAssistManager,
@@ -182,9 +175,9 @@
                 mKeyguardStateController,
                 mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
                 mock(LockPatternUtils.class), mHandler, mHandler, mUiBgExecutor,
-                mActivityIntentHelper, mBubbleController, mShadeController,
-                mSuperStatusBarViewFactory))
+                mActivityIntentHelper, mBubbleController, mShadeController))
                 .setStatusBar(mStatusBar)
+                .setNotificationPanelViewController(mock(NotificationPanelViewController.class))
                 .setNotificationPresenter(mock(NotificationPresenter.class))
                 .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
         .build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1296a97..5ac7bfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -108,7 +108,7 @@
         StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
         when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
         mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
-                mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
+                mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
                 statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
                 mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 1cdba47..7e485f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -124,6 +124,7 @@
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -172,6 +173,7 @@
     @Mock private KeyguardIndicationController mKeyguardIndicationController;
     @Mock private NotificationStackScrollLayout mStackScroller;
     @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationPanelViewController mNotificationPanelViewController;
     @Mock private NotificationPanelView mNotificationPanelView;
     @Mock private IStatusBarService mBarService;
     @Mock private IDreamManager mDreamManager;
@@ -285,6 +287,7 @@
         mContext.setTheme(R.style.Theme_SystemUI_Light);
 
         when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
+        when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView);
         when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
         when(powerManagerService.isInteractive()).thenReturn(true);
         when(mStackScroller.getActivatedChild()).thenReturn(null);
@@ -416,7 +419,7 @@
                 mLockIconContainer);
 
         when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
-                any(NotificationPanelView.class), any(BiometricUnlockController.class),
+                any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
                 any(ViewGroup.class), any(ViewGroup.class), any(KeyguardBypassController.class)))
                 .thenReturn(mStatusBarKeyguardViewManager);
 
@@ -426,7 +429,7 @@
         // TODO: we should be able to call mStatusBar.start() and have all the below values
         // initialized automatically.
         mStatusBar.mStatusBarWindow = mStatusBarWindowView;
-        mStatusBar.mNotificationPanel = mNotificationPanelView;
+        mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
         mStatusBar.mDozeScrimController = mDozeScrimController;
         mStatusBar.mNotificationIconAreaController = mNotificationIconAreaController;
         mStatusBar.mPresenter = mNotificationPresenter;
@@ -731,20 +734,20 @@
         when(mCommandQueue.panelsEnabled()).thenReturn(false);
         mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
                 StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
-        verify(mNotificationPanelView).setQsExpansionEnabled(false);
+        verify(mNotificationPanelViewController).setQsExpansionEnabled(false);
         mStatusBar.animateExpandNotificationsPanel();
-        verify(mNotificationPanelView, never()).expand(anyBoolean());
+        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
         mStatusBar.animateExpandSettingsPanel(null);
-        verify(mNotificationPanelView, never()).expand(anyBoolean());
+        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
 
         when(mCommandQueue.panelsEnabled()).thenReturn(true);
         mStatusBar.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
                 StatusBarManager.DISABLE2_NONE, false);
-        verify(mNotificationPanelView).setQsExpansionEnabled(true);
+        verify(mNotificationPanelViewController).setQsExpansionEnabled(true);
         mStatusBar.animateExpandNotificationsPanel();
-        verify(mNotificationPanelView).expandWithoutQs();
+        verify(mNotificationPanelViewController).expandWithoutQs();
         mStatusBar.animateExpandSettingsPanel(null);
-        verify(mNotificationPanelView).expandWithQs();
+        verify(mNotificationPanelViewController).expandWithQs();
     }
 
     @Test
@@ -834,12 +837,12 @@
         when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
         mStatusBar.updateIsKeyguard();
         // TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
-        verify(mNotificationPanelView, times(2)).expand(eq(false));
-        clearInvocations(mNotificationPanelView);
+        verify(mNotificationPanelViewController, times(2)).expand(eq(false));
+        clearInvocations(mNotificationPanelViewController);
 
         mStatusBar.mWakefulnessObserver.onStartedWakingUp();
         verify(mDozeServiceHost).stopDozing();
-        verify(mNotificationPanelView).expand(eq(false));
+        verify(mNotificationPanelViewController).expand(eq(false));
     }
 
     @Test
@@ -848,11 +851,11 @@
         when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
         when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
         mStatusBar.updateIsKeyguard();
-        clearInvocations(mNotificationPanelView);
+        clearInvocations(mNotificationPanelViewController);
 
         mStatusBar.setBouncerShowing(true);
         mStatusBar.mWakefulnessObserver.onStartedWakingUp();
-        verify(mNotificationPanelView, never()).expand(anyBoolean());
+        verify(mNotificationPanelViewController, never()).expand(anyBoolean());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index 9f899ee..f9848f3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -26,6 +27,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManagerFake;
@@ -40,6 +42,7 @@
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
@@ -74,12 +77,16 @@
     @Mock private DozeLog mDozeLog;
     @Mock private DozeParameters mDozeParameters;
     @Mock private DockManager mDockManager;
+    @Mock private NotificationPanelViewController mNotificationPanelViewController;
+    @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mView = new StatusBarWindowView(getContext(), null);
+        mView = spy(new StatusBarWindowView(getContext(), null));
+        when(mView.findViewById(R.id.notification_stack_scroller))
+                .thenReturn(mNotificationStackScrollLayout);
         when(mStatusBarStateController.isDozing()).thenReturn(false);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
@@ -104,7 +111,8 @@
                 new CommandQueue(mContext),
                 mShadeController,
                 mDockManager,
-                mView);
+                mView,
+                mNotificationPanelViewController);
         mController.setupExpandedStatusBar();
         mController.setService(mStatusBar);
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 3451183..32da4c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -39,7 +39,8 @@
 
         setWifiState(true, testSsid);
         setWifiLevel(0);
-        verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
+        // Connected, but still not validated - does not show
+        verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
 
         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
             setWifiLevel(testLevel);
@@ -47,7 +48,8 @@
             setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
             verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
             setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
-            verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
+            // Icon does not show if not validated
+            verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
         }
     }
 
@@ -70,7 +72,7 @@
                     testSsid);
             setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
             verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
-                    testSsid);
+                    null);
         }
     }
 
@@ -132,7 +134,7 @@
         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
 
         setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true);
-        verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
+        verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
index e15ca1d..27b225e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeSensorManager.java
@@ -30,14 +30,13 @@
 import android.os.SystemClock;
 import android.util.ArraySet;
 
-import com.android.internal.util.Preconditions;
-
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.stream.Collectors;
 
 import javax.annotation.Nullable;
@@ -113,7 +112,7 @@
 
     @Override
     protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
-        Preconditions.checkNotNull(listener);
+        Objects.requireNonNull(listener);
         for (FakeGenericSensor s : mSensors) {
             if (sensor == null || s.mSensor == sensor) {
                 s.mListeners.remove(listener);
@@ -125,8 +124,8 @@
     protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,
             int delayUs,
             Handler handler, int maxReportLatencyUs, int reservedFlags) {
-        Preconditions.checkNotNull(sensor);
-        Preconditions.checkNotNull(listener);
+        Objects.requireNonNull(sensor);
+        Objects.requireNonNull(listener);
         for (FakeGenericSensor s : mSensors) {
             if (s.mSensor == sensor) {
                 s.mListeners.add(listener);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index df76f01..8ec4cb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -45,6 +45,11 @@
     }
 
     @Override
+    public boolean isPluggedIn() {
+        return false;
+    }
+
+    @Override
     public boolean isPowerSave() {
         return false;
     }
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 08552cb..0be853a 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -32,6 +32,7 @@
     ],
     libs: [
         "framework-tethering",
+        "unsupportedappusage",
     ],
 
     manifest: "AndroidManifestBase.xml",
@@ -45,9 +46,9 @@
 
 // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
 cc_library {
-    name: "libtetheroffloadjni",
+    name: "libtetherutilsjni",
     srcs: [
-        "jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
+        "jni/android_net_util_TetheringUtils.cpp",
     ],
     shared_libs: [
         "libcgrouprc",
@@ -87,7 +88,7 @@
         "libcgrouprc",
         "libnativehelper_compat_libc++",
         "libvndksupport",
-        "libtetheroffloadjni",
+        "libtetherutilsjni",
     ],
     resource_dirs: [
         "res",
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index dd9eab7..d93531b 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -13,3 +13,5 @@
 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
 
 rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
+
+rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
new file mode 100644
index 0000000..1cf8f98
--- /dev/null
+++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -0,0 +1,256 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <error.h>
+#include <hidl/HidlSupport.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <net/if.h>
+#include <netinet/icmp6.h>
+#include <sys/socket.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+
+#define LOG_TAG "TetheringUtils"
+#include <utils/Log.h>
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_string;
+using hardware::tetheroffload::config::V1_0::IOffloadConfig;
+
+namespace {
+
+inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
+    return reinterpret_cast<const sockaddr *>(nladdr);
+}
+
+int conntrackSocket(unsigned groups) {
+    base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
+    if (s.get() < 0) return -errno;
+
+    const struct sockaddr_nl bind_addr = {
+        .nl_family = AF_NETLINK,
+        .nl_pad = 0,
+        .nl_pid = 0,
+        .nl_groups = groups,
+    };
+    if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
+        return -errno;
+    }
+
+    const struct sockaddr_nl kernel_addr = {
+        .nl_family = AF_NETLINK,
+        .nl_pad = 0,
+        .nl_pid = 0,
+        .nl_groups = groups,
+    };
+    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
+        return -errno;
+    }
+
+    return s.release();
+}
+
+// Return a hidl_handle that owns the file descriptor owned by fd, and will
+// auto-close it (otherwise there would be double-close problems).
+//
+// Rely upon the compiler to eliminate the constexprs used for clarity.
+hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
+    hidl_handle h;
+
+    static constexpr int kNumFds = 1;
+    static constexpr int kNumInts = 0;
+    native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
+    nh->data[0] = fd.release();
+
+    static constexpr bool kTakeOwnership = true;
+    h.setTo(nh, kTakeOwnership);
+
+    return h;
+}
+
+}  // namespace
+
+static jboolean android_net_util_configOffload(
+        JNIEnv* /* env */) {
+    sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
+    if (configInterface.get() == nullptr) {
+        ALOGD("Could not find IOffloadConfig service.");
+        return false;
+    }
+
+    // Per the IConfigOffload definition:
+    //
+    // fd1   A file descriptor bound to the following netlink groups
+    //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+    //
+    // fd2   A file descriptor bound to the following netlink groups
+    //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+    base::unique_fd
+            fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
+            fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
+    if (fd1.get() < 0 || fd2.get() < 0) {
+        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+        return false;
+    }
+
+    hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
+                h2(handleFromFileDescriptor(std::move(fd2)));
+
+    bool rval(false);
+    hidl_string msg;
+    const auto status = configInterface->setHandles(h1, h2,
+            [&rval, &msg](bool success, const hidl_string& errMsg) {
+                rval = success;
+                msg = errMsg;
+            });
+    if (!status.isOk() || !rval) {
+        ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
+              status.description().c_str(), msg.c_str());
+        // If status is somehow not ok, make sure rval captures this too.
+        rval = false;
+    }
+
+    return rval;
+}
+
+static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
+        jint ifIndex)
+{
+    static const int kLinkLocalHopLimit = 255;
+
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+
+    // Set an ICMPv6 filter that only passes Router Solicitations.
+    struct icmp6_filter rs_only;
+    ICMP6_FILTER_SETBLOCKALL(&rs_only);
+    ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &rs_only);
+    socklen_t len = sizeof(rs_only);
+    if (setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &rs_only, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(ICMP6_FILTER): %s", strerror(errno));
+        return;
+    }
+
+    // Most/all of the rest of these options can be set via Java code, but
+    // because we're here on account of setting an icmp6_filter go ahead
+    // and do it all natively for now.
+
+    // Set the multicast hoplimit to 255 (link-local only).
+    int hops = kLinkLocalHopLimit;
+    len = sizeof(hops);
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(IPV6_MULTICAST_HOPS): %s", strerror(errno));
+        return;
+    }
+
+    // Set the unicast hoplimit to 255 (link-local only).
+    hops = kLinkLocalHopLimit;
+    len = sizeof(hops);
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(IPV6_UNICAST_HOPS): %s", strerror(errno));
+        return;
+    }
+
+    // Explicitly disable multicast loopback.
+    int off = 0;
+    len = sizeof(off);
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(IPV6_MULTICAST_LOOP): %s", strerror(errno));
+        return;
+    }
+
+    // Specify the IPv6 interface to use for outbound multicast.
+    len = sizeof(ifIndex);
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifIndex, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(IPV6_MULTICAST_IF): %s", strerror(errno));
+        return;
+    }
+
+    // Additional options to be considered:
+    //     - IPV6_TCLASS
+    //     - IPV6_RECVPKTINFO
+    //     - IPV6_RECVHOPLIMIT
+
+    // Bind to [::].
+    const struct sockaddr_in6 sin6 = {
+            .sin6_family = AF_INET6,
+            .sin6_port = 0,
+            .sin6_flowinfo = 0,
+            .sin6_addr = IN6ADDR_ANY_INIT,
+            .sin6_scope_id = 0,
+    };
+    auto sa = reinterpret_cast<const struct sockaddr *>(&sin6);
+    len = sizeof(sin6);
+    if (bind(fd, sa, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "bind(IN6ADDR_ANY): %s", strerror(errno));
+        return;
+    }
+
+    // Join the all-routers multicast group, ff02::2%index.
+    struct ipv6_mreq all_rtrs = {
+        .ipv6mr_multiaddr = {{{0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}},
+        .ipv6mr_interface = ifIndex,
+    };
+    len = sizeof(all_rtrs);
+    if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &all_rtrs, len) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(IPV6_JOIN_GROUP): %s", strerror(errno));
+        return;
+    }
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gMethods[] = {
+    /* name, signature, funcPtr */
+    { "configOffload", "()Z", (void*) android_net_util_configOffload },
+    { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
+};
+
+int register_android_net_util_TetheringUtils(JNIEnv* env) {
+    return jniRegisterNativeMethods(env,
+            "android/net/util/TetheringUtils",
+            gMethods, NELEM(gMethods));
+}
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+    JNIEnv *env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        ALOGE("ERROR: GetEnv failed");
+        return JNI_ERR;
+    }
+
+    if (register_android_net_util_TetheringUtils(env) < 0) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_6;
+}
+
+}; // namespace android
diff --git a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
deleted file mode 100644
index 663154a..0000000
--- a/packages/Tethering/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ /dev/null
@@ -1,162 +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.
- */
-
-#include <errno.h>
-#include <error.h>
-#include <hidl/HidlSupport.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
-#include <sys/socket.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
-
-#define LOG_TAG "OffloadHardwareInterface"
-#include <utils/Log.h>
-
-namespace android {
-
-using hardware::hidl_handle;
-using hardware::hidl_string;
-using hardware::tetheroffload::config::V1_0::IOffloadConfig;
-
-namespace {
-
-inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
-    return reinterpret_cast<const sockaddr *>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
-    base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
-    if (s.get() < 0) return -errno;
-
-    const struct sockaddr_nl bind_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl kernel_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
-        return -errno;
-    }
-
-    return s.release();
-}
-
-// Return a hidl_handle that owns the file descriptor owned by fd, and will
-// auto-close it (otherwise there would be double-close problems).
-//
-// Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
-    hidl_handle h;
-
-    static constexpr int kNumFds = 1;
-    static constexpr int kNumInts = 0;
-    native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
-    nh->data[0] = fd.release();
-
-    static constexpr bool kTakeOwnership = true;
-    h.setTo(nh, kTakeOwnership);
-
-    return h;
-}
-
-}  // namespace
-
-static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_configOffload(
-        JNIEnv* /* env */) {
-    sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
-    if (configInterface.get() == nullptr) {
-        ALOGD("Could not find IOffloadConfig service.");
-        return false;
-    }
-
-    // Per the IConfigOffload definition:
-    //
-    // fd1   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
-    //
-    // fd2   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
-    base::unique_fd
-            fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
-            fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
-    if (fd1.get() < 0 || fd2.get() < 0) {
-        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-        return false;
-    }
-
-    hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
-                h2(handleFromFileDescriptor(std::move(fd2)));
-
-    bool rval(false);
-    hidl_string msg;
-    const auto status = configInterface->setHandles(h1, h2,
-            [&rval, &msg](bool success, const hidl_string& errMsg) {
-                rval = success;
-                msg = errMsg;
-            });
-    if (!status.isOk() || !rval) {
-        ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
-              status.description().c_str(), msg.c_str());
-        // If status is somehow not ok, make sure rval captures this too.
-        rval = false;
-    }
-
-    return rval;
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
-    /* name, signature, funcPtr */
-    { "configOffload", "()Z",
-      (void*) android_server_connectivity_tethering_OffloadHardwareInterface_configOffload },
-};
-
-int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv* env) {
-    return jniRegisterNativeMethods(env,
-            "com/android/server/connectivity/tethering/OffloadHardwareInterface",
-            gMethods, NELEM(gMethods));
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
-    JNIEnv *env;
-    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        ALOGE("ERROR: GetEnv failed");
-        return JNI_ERR;
-    }
-
-    if (register_android_server_connectivity_tethering_OffloadHardwareInterface(env) < 0) {
-        return JNI_ERR;
-    }
-
-    return JNI_VERSION_1_6;
-}
-
-}; // namespace android
diff --git a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
index 1fe2328..d6bc063 100644
--- a/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
+++ b/packages/Tethering/src/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -20,11 +20,11 @@
 
 import android.annotation.NonNull;
 import android.net.LinkAddress;
-
-import com.google.android.collect.Sets;
+import android.util.ArraySet;
 
 import java.net.Inet4Address;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 
 /**
@@ -68,7 +68,7 @@
      * but it must always be set explicitly.
      */
     public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
-        return setDefaultRouters(Sets.newArraySet(defaultRouters));
+        return setDefaultRouters(newArraySet(defaultRouters));
     }
 
     /**
@@ -96,7 +96,7 @@
      * <p>This may be an empty list of servers, but it must always be set explicitly.
      */
     public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
-        return setDnsServers(Sets.newArraySet(dnsServers));
+        return setDnsServers(newArraySet(dnsServers));
     }
 
     /**
@@ -126,7 +126,7 @@
      * and do not need to be set here.
      */
     public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
-        return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+        return setExcludedAddrs(newArraySet(excludedAddrs));
     }
 
     /**
@@ -169,4 +169,10 @@
         }
         return res;
     }
+
+    private static ArraySet<Inet4Address> newArraySet(Inet4Address... addrs) {
+        ArraySet<Inet4Address> addrSet = new ArraySet<>(addrs.length);
+        Collections.addAll(addrSet, addrs);
+        return addrSet;
+    }
 }
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 8fde520..abfb33c 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -17,10 +17,12 @@
 package android.net.ip;
 
 import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.util.NetworkConstants.FF;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
 import static android.net.util.NetworkConstants.asByte;
+import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
 
 import android.net.ConnectivityManager;
 import android.net.INetd;
@@ -46,11 +48,9 @@
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
@@ -153,27 +153,26 @@
                 DhcpServerCallbacks cb);
     }
 
-    private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
     // request from the user that it wants to tether
-    public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
+    public static final int CMD_TETHER_REQUESTED            = BASE_IPSERVER + 1;
     // request from the user that it wants to untether
-    public static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
+    public static final int CMD_TETHER_UNREQUESTED          = BASE_IPSERVER + 2;
     // notification that this interface is down
-    public static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
+    public static final int CMD_INTERFACE_DOWN              = BASE_IPSERVER + 3;
     // notification from the master SM that it had trouble enabling IP Forwarding
-    public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
+    public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IPSERVER + 4;
     // notification from the master SM that it had trouble disabling IP Forwarding
-    public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
+    public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IPSERVER + 5;
     // notification from the master SM that it had trouble starting tethering
-    public static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
+    public static final int CMD_START_TETHERING_ERROR       = BASE_IPSERVER + 6;
     // notification from the master SM that it had trouble stopping tethering
-    public static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
+    public static final int CMD_STOP_TETHERING_ERROR        = BASE_IPSERVER + 7;
     // notification from the master SM that it had trouble setting the DNS forwarders
-    public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
+    public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IPSERVER + 8;
     // the upstream connection has changed
-    public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
+    public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IPSERVER + 9;
     // new IPv6 tethering parameters need to be processed
-    public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
+    public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IPSERVER + 10;
 
     private final State mInitialState;
     private final State mLocalHotspotState;
@@ -486,7 +485,9 @@
         }
 
         // Directly-connected route.
-        final RouteInfo route = new RouteInfo(linkAddr);
+        final IpPrefix ipv4Prefix = new IpPrefix(linkAddr.getAddress(),
+                linkAddr.getPrefixLength());
+        final RouteInfo route = new RouteInfo(ipv4Prefix, null, null, RTN_UNICAST);
         if (enabled) {
             mLinkProperties.addLinkAddress(linkAddr);
             mLinkProperties.addRoute(route);
@@ -1007,7 +1008,7 @@
             String ifname, HashSet<IpPrefix> prefixes) {
         final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
         for (IpPrefix ipp : prefixes) {
-            localRoutes.add(new RouteInfo(ipp, null, ifname));
+            localRoutes.add(new RouteInfo(ipp, null, ifname, RTN_UNICAST));
         }
         return localRoutes;
     }
@@ -1019,7 +1020,7 @@
         try {
             return Inet6Address.getByAddress(null, dnsBytes, 0);
         } catch (UnknownHostException e) {
-            Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
+            Log.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
             return null;
         }
     }
diff --git a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 4147413..bba61d7 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -22,14 +22,14 @@
 import static android.system.OsConstants.IPPROTO_ICMPV6;
 import static android.system.OsConstants.SOCK_RAW;
 import static android.system.OsConstants.SOL_SOCKET;
-import static android.system.OsConstants.SO_BINDTODEVICE;
 import static android.system.OsConstants.SO_SNDTIMEO;
 
 import android.net.IpPrefix;
 import android.net.LinkAddress;
-import android.net.NetworkUtils;
 import android.net.TrafficStats;
 import android.net.util.InterfaceParams;
+import android.net.util.SocketUtils;
+import android.net.util.TetheringUtils;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructTimeval;
@@ -38,8 +38,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.TrafficStatsConstants;
 
-import libcore.io.IoBridge;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.Inet6Address;
@@ -611,9 +609,8 @@
             // Setting SNDTIMEO is purely for defensive purposes.
             Os.setsockoptTimeval(
                     mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(send_timout_ms));
-            Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mInterface.name);
-            NetworkUtils.protectFromVpn(mSocket);
-            NetworkUtils.setupRaSocket(mSocket, mInterface.index);
+            SocketUtils.bindSocketToInterface(mSocket, mInterface.name);
+            TetheringUtils.setupRaSocket(mSocket, mInterface.index);
         } catch (ErrnoException | IOException e) {
             Log.e(TAG, "Failed to create RA daemon socket: " + e);
             return false;
@@ -627,7 +624,7 @@
     private void closeSocket() {
         if (mSocket != null) {
             try {
-                IoBridge.closeAndSignalBlockedThreads(mSocket);
+                SocketUtils.closeSocket(mSocket);
             } catch (IOException ignored) { }
         }
         mSocket = null;
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/packages/Tethering/src/android/net/util/TetheringMessageBase.java
similarity index 64%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to packages/Tethering/src/android/net/util/TetheringMessageBase.java
index 1fefda4..1b763ce 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/packages/Tethering/src/android/net/util/TetheringMessageBase.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package android.net.util;
 
-package android.service.controls.templates;
+/**
+ * This class defines Message.what base addresses for various state machine.
+ */
+public class TetheringMessageBase {
+    public static final int BASE_MASTER   = 0;
+    public static final int BASE_IPSERVER = 100;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+}
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
new file mode 100644
index 0000000..fa543bd
--- /dev/null
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.util;
+
+import java.io.FileDescriptor;
+import java.net.SocketException;
+
+/**
+ * Native methods for tethering utilization.
+ *
+ * {@hide}
+ */
+public class TetheringUtils {
+
+    /**
+     * Offload management process need to know conntrack rules to support NAT, but it may not have
+     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
+     * share them with offload management process.
+     */
+    public static native boolean configOffload();
+
+    /**
+     * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
+     * @param fd the socket's {@link FileDescriptor}.
+     * @param ifIndex the interface index.
+     */
+    public static native void setupRaSocket(FileDescriptor fd, int ifIndex)
+            throws SocketException;
+
+    /**
+     * Read s as an unsigned 16-bit integer.
+     */
+    public static int uint16(short s) {
+        return s & 0xffff;
+    }
+}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index ba5d08d..7e685fb 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -38,7 +38,6 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.net.util.SharedLog;
-import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -48,7 +47,6 @@
 import android.os.ResultReceiver;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.util.ArraySet;
@@ -196,9 +194,9 @@
             // till upstream change to cellular.
             if (mUsingCellularAsUpstream) {
                 if (showProvisioningUi) {
-                    runUiTetherProvisioning(type, config.subId);
+                    runUiTetherProvisioning(type, config.activeDataSubId);
                 } else {
-                    runSilentTetherProvisioning(type, config.subId);
+                    runSilentTetherProvisioning(type, config.activeDataSubId);
                 }
                 mNeedReRunProvisioningUi = false;
             } else {
@@ -270,9 +268,9 @@
             if (mCellularPermitted.indexOfKey(downstream) < 0) {
                 if (mNeedReRunProvisioningUi) {
                     mNeedReRunProvisioningUi = false;
-                    runUiTetherProvisioning(downstream, config.subId);
+                    runUiTetherProvisioning(downstream, config.activeDataSubId);
                 } else {
-                    runSilentTetherProvisioning(downstream, config.subId);
+                    runSilentTetherProvisioning(downstream, config.activeDataSubId);
                 }
             }
         }
@@ -336,7 +334,8 @@
                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (configManager == null) return null;
 
-        final PersistableBundle carrierConfig = configManager.getConfigForSubId(config.subId);
+        final PersistableBundle carrierConfig = configManager.getConfigForSubId(
+                config.activeDataSubId);
 
         if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
             return carrierConfig;
@@ -379,12 +378,9 @@
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
         intent.putExtra(EXTRA_SUBID, subId);
         intent.setComponent(TETHER_SERVICE);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startServiceAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        // Only admin user can change tethering and SilentTetherProvisioning don't need to
+        // show UI, it is fine to always start setting's background service as system user.
+        mContext.startService(intent);
     }
 
     private void runUiTetherProvisioning(int type, int subId) {
@@ -407,12 +403,9 @@
         intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
         intent.putExtra(EXTRA_SUBID, subId);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
+        // Only launch entitlement UI for system user. Entitlement UI should not appear for other
+        // user because only admin user is allowed to change tethering.
+        mContext.startActivity(intent);
     }
 
     // Not needed to check if this don't run on the handler thread because it's private.
@@ -671,7 +664,7 @@
             receiver.send(cacheValue, null);
         } else {
             ResultReceiver proxy = buildProxyReceiver(downstream, false/* notifyFail */, receiver);
-            runUiTetherProvisioning(downstream, config.subId, proxy);
+            runUiTetherProvisioning(downstream, config.activeDataSubId, proxy);
         }
     }
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 9305414..66b9ade 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -29,6 +29,7 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -257,7 +258,7 @@
         final LinkProperties lp = new LinkProperties();
 
         final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
-        lp.addRoute(new RouteInfo(local48, null, null));
+        lp.addRoute(new RouteInfo(local48, null, null, RouteInfo.RTN_UNICAST));
 
         final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
         // Because this is a locally-generated ULA, we don't have an upstream
@@ -273,7 +274,13 @@
         final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
         bytes[7] = (byte) (subnetId >> 8);
         bytes[8] = (byte) subnetId;
-        return new IpPrefix(bytes, prefixlen);
+        final InetAddress addr;
+        try {
+            addr = InetAddress.getByAddress(bytes);
+        } catch (UnknownHostException e) {
+            throw new IllegalStateException("Invalid address length: " + bytes.length, e);
+        }
+        return new IpPrefix(addr, prefixlen);
     }
 
     // Generates a Unique Locally-assigned Prefix:
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index 16734d8..38fa91e 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -25,6 +25,7 @@
 
 import android.content.ContentResolver;
 import android.net.ITetheringStatsProvider;
+import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -33,7 +34,6 @@
 import android.net.netlink.ConntrackMessage;
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.NetlinkSocket;
-import android.net.util.IpUtils;
 import android.net.util.SharedLog;
 import android.os.Handler;
 import android.os.INetworkManagementService;
@@ -477,9 +477,10 @@
             if (!ri.hasGateway()) continue;
 
             final String gateway = ri.getGateway().getHostAddress();
-            if (ri.isIPv4Default()) {
+            final InetAddress address = ri.getDestination().getAddress();
+            if (ri.isDefaultRoute() && address instanceof Inet4Address) {
                 v4gateway = gateway;
-            } else if (ri.isIPv6Default()) {
+            } else if (ri.isDefaultRoute() && address instanceof Inet6Address) {
                 v6gateways.add(gateway);
             }
         }
@@ -547,7 +548,10 @@
 
     private static boolean shouldIgnoreDownstreamRoute(RouteInfo route) {
         // Ignore any link-local routes.
-        if (!route.getDestinationLinkAddress().isGlobalPreferred()) return true;
+        final IpPrefix destination = route.getDestination();
+        final LinkAddress linkAddr = new LinkAddress(destination.getAddress(),
+                destination.getPrefixLength());
+        if (!linkAddr.isGlobalPreferred()) return true;
 
         return false;
     }
@@ -588,7 +592,7 @@
             return;
         }
 
-        if (!IpUtils.isValidUdpOrTcpPort(srcPort)) {
+        if (!isValidUdpOrTcpPort(srcPort)) {
             mLog.e("Invalid src port: " + srcPort);
             return;
         }
@@ -599,7 +603,7 @@
             return;
         }
 
-        if (!IpUtils.isValidUdpOrTcpPort(dstPort)) {
+        if (!isValidUdpOrTcpPort(dstPort)) {
             mLog.e("Invalid dst port: " + dstPort);
             return;
         }
@@ -628,7 +632,7 @@
 
     private static Inet4Address parseIPv4Address(String addrString) {
         try {
-            final InetAddress ip = InetAddress.parseNumericAddress(addrString);
+            final InetAddress ip = InetAddresses.parseNumericAddress(addrString);
             // TODO: Consider other sanitization steps here, including perhaps:
             //           not eql to 0.0.0.0
             //           not within 169.254.0.0/16
@@ -668,4 +672,8 @@
             return 180;
         }
     }
+
+    private static boolean isValidUdpOrTcpPort(int port) {
+        return port > 0 && port < 65536;
+    }
 }
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 00a6773..4a8ef1f 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -16,7 +16,7 @@
 
 package com.android.server.connectivity.tethering;
 
-import static com.android.internal.util.BitUtils.uint16;
+import static android.net.util.TetheringUtils.uint16;
 
 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
@@ -24,6 +24,7 @@
 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
 import android.net.util.SharedLog;
+import android.net.util.TetheringUtils;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.system.OsConstants;
@@ -47,8 +48,6 @@
     private static final String NO_IPV4_ADDRESS = "";
     private static final String NO_IPV4_GATEWAY = "";
 
-    private static native boolean configOffload();
-
     private final Handler mHandler;
     private final SharedLog mLog;
     private IOffloadControl mOffloadControl;
@@ -107,8 +106,6 @@
     public OffloadHardwareInterface(Handler h, SharedLog log) {
         mHandler = h;
         mLog = log.forSubComponent(TAG);
-
-        System.loadLibrary("tetheroffloadjni");
     }
 
     /** Get default value indicating whether offload is supported. */
@@ -118,7 +115,7 @@
 
     /** Configure offload management process. */
     public boolean initOffloadConfig() {
-        return configOffload();
+        return TetheringUtils.configOffload();
     }
 
     /** Initialize the tethering offload HAL. */
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 3d414ee..5b26704 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -37,6 +37,7 @@
 import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
 import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
 import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.util.TetheringMessageBase.BASE_MASTER;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -106,7 +107,6 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.MessageUtils;
-import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 import com.android.networkstack.tethering.R;
@@ -120,6 +120,8 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
 
 /**
  *
@@ -185,10 +187,10 @@
     private final TetheringDependencies mDeps;
     private final EntitlementManager mEntitlementMgr;
     private final Handler mHandler;
-    private final PhoneStateListener mPhoneStateListener;
     private final INetd mNetd;
     private final NetdCallback mNetdCallback;
     private final UserRestrictionActionListener mTetheringRestriction;
+    private final ActiveDataSubIdListener mActiveDataSubIdListener;
     private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
     // All the usage of mTetheringEventCallback should run in the same thread.
     private ITetheringEventCallback mTetheringEventCallback = null;
@@ -252,26 +254,6 @@
                     mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
                 });
 
-        mPhoneStateListener = new PhoneStateListener(mLooper) {
-            @Override
-            public void onActiveDataSubscriptionIdChanged(int subId) {
-                mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId
-                        + " to " + subId);
-                if (subId == mActiveDataSubId) return;
-
-                mActiveDataSubId = subId;
-                updateConfiguration();
-                // To avoid launching unexpected provisioning checks, ignore re-provisioning when
-                // no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning() will be
-                // triggered again when CarrierConfig is loaded.
-                if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
-                    mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
-                } else {
-                    mLog.log("IGNORED reevaluate provisioning due to no carrier config loaded");
-                }
-            }
-        };
-
         mStateReceiver = new StateReceiver();
 
         mNetdCallback = new NetdCallback();
@@ -284,6 +266,8 @@
         final UserManager userManager = (UserManager) mContext.getSystemService(
                     Context.USER_SERVICE);
         mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
+        final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
+        mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
 
         // Load tethering configuration.
         updateConfiguration();
@@ -294,8 +278,8 @@
 
     private void startStateMachineUpdaters(Handler handler) {
         mCarrierConfigChange.startListening();
-        mContext.getSystemService(TelephonyManager.class).listen(
-                mPhoneStateListener, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+        mContext.getSystemService(TelephonyManager.class).listen(mActiveDataSubIdListener,
+                PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(UsbManager.ACTION_USB_STATE);
@@ -314,6 +298,43 @@
 
     }
 
+    private class TetheringThreadExecutor implements Executor {
+        private final Handler mTetherHandler;
+        TetheringThreadExecutor(Handler handler) {
+            mTetherHandler = handler;
+        }
+        @Override
+        public void execute(Runnable command) {
+            if (!mTetherHandler.post(command)) {
+                throw new RejectedExecutionException(mTetherHandler + " is shutting down");
+            }
+        }
+    }
+
+    private class ActiveDataSubIdListener extends PhoneStateListener {
+        ActiveDataSubIdListener(Executor executor) {
+            super(executor);
+        }
+
+        @Override
+        public void onActiveDataSubscriptionIdChanged(int subId) {
+            mLog.log("OBSERVED active data subscription change, from " + mActiveDataSubId
+                    + " to " + subId);
+            if (subId == mActiveDataSubId) return;
+
+            mActiveDataSubId = subId;
+            updateConfiguration();
+            // To avoid launching unexpected provisioning checks, ignore re-provisioning
+            // when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
+            // ill be triggered again when CarrierConfig is loaded.
+            if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
+                mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
+            } else {
+                mLog.log("IGNORED reevaluate provisioning, no carrier config loaded");
+            }
+        }
+    }
+
     private WifiManager getWifiManager() {
         return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
     }
@@ -326,8 +347,7 @@
     }
 
     private void maybeDunSettingChanged() {
-        final boolean isDunRequired = TetheringConfiguration.checkDunRequired(
-                mContext, mActiveDataSubId);
+        final boolean isDunRequired = TetheringConfiguration.checkDunRequired(mContext);
         if (isDunRequired == mConfig.isDunRequired) return;
         updateConfiguration();
     }
@@ -633,8 +653,7 @@
         reportTetherStateChanged(mTetherStatesParcel);
 
         final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
-        bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
-                | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
         bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
         bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
         bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
@@ -1163,7 +1182,6 @@
     }
 
     class TetherMasterSM extends StateMachine {
-        private static final int BASE_MASTER                    = Protocol.BASE_TETHERING;
         // an interface SM has requested Tethering/Local Hotspot
         static final int EVENT_IFACE_SERVING_STATE_ACTIVE       = BASE_MASTER + 1;
         // an interface SM has unrequested Tethering/Local Hotspot
@@ -1180,7 +1198,6 @@
         static final int EVENT_IFACE_UPDATE_LINKPROPERTIES      = BASE_MASTER + 7;
         // Events from EntitlementManager to choose upstream again.
         static final int EVENT_UPSTREAM_PERMISSION_CHANGED      = BASE_MASTER + 8;
-
         private final State mInitialState;
         private final State mTetherModeAliveState;
 
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 0ab4d63..490614b 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -100,13 +100,13 @@
     public final String provisioningAppNoUi;
     public final int provisioningCheckPeriod;
 
-    public final int subId;
+    public final int activeDataSubId;
 
     public TetheringConfiguration(Context ctx, SharedLog log, int id) {
         final SharedLog configLog = log.forSubComponent("config");
 
-        subId = id;
-        Resources res = getResources(ctx, subId);
+        activeDataSubId = id;
+        Resources res = getResources(ctx, activeDataSubId);
 
         tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
         // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
@@ -116,7 +116,7 @@
         tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
         tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
 
-        isDunRequired = checkDunRequired(ctx, subId);
+        isDunRequired = checkDunRequired(ctx);
 
         chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
         preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
@@ -166,8 +166,8 @@
 
     /** Does the dumping.*/
     public void dump(PrintWriter pw) {
-        pw.print("subId: ");
-        pw.println(subId);
+        pw.print("activeDataSubId: ");
+        pw.println(activeDataSubId);
 
         dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
         dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
@@ -196,7 +196,7 @@
     /** Returns the string representation of this object.*/
     public String toString() {
         final StringJoiner sj = new StringJoiner(" ");
-        sj.add(String.format("subId:%d", subId));
+        sj.add(String.format("activeDataSubId:%d", activeDataSubId));
         sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
         sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
         sj.add(String.format("tetherableWifiP2pRegexs:%s", makeString(tetherableWifiP2pRegexs)));
@@ -250,9 +250,11 @@
     }
 
     /** Check whether dun is required. */
-    public static boolean checkDunRequired(Context ctx, int id) {
+    public static boolean checkDunRequired(Context ctx) {
         final TelephonyManager tm = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
-        return (tm != null) ? tm.isTetheringApnRequired(id) : false;
+        // TelephonyManager would uses the active data subscription, which should be the one used
+        // by tethering.
+        return (tm != null) ? tm.isTetheringApnRequired() : false;
     }
 
     private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
@@ -391,7 +393,7 @@
      */
     public TetheringConfigurationParcel toStableParcelable() {
         final TetheringConfigurationParcel parcel = new TetheringConfigurationParcel();
-        parcel.subId = subId;
+        parcel.subId = activeDataSubId;
         parcel.tetherableUsbRegexs = tetherableUsbRegexs;
         parcel.tetherableWifiRegexs = tetherableWifiRegexs;
         parcel.tetherableBluetoothRegexs = tetherableBluetoothRegexs;
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index ba30845..775484e 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -21,6 +21,7 @@
 import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
 import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
+import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
 
 import android.app.Service;
 import android.content.Context;
@@ -84,6 +85,7 @@
      */
     @VisibleForTesting
     public Tethering makeTethering(TetheringDependencies deps) {
+        System.loadLibrary("tetherutilsjni");
         return new Tethering(deps);
     }
 
@@ -339,7 +341,10 @@
 
                                 service.makeDhcpServer(ifName, params, cb);
                             } catch (RemoteException e) {
-                                e.rethrowFromSystemServer();
+                                Log.e(TAG, "Fail to make dhcp server");
+                                try {
+                                    cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+                                } catch (RemoteException re) { }
                             }
                         }
                     };
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index dc38c49a..22150f6 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -328,13 +328,6 @@
                     network, newNc));
         }
 
-        // Log changes in upstream network signal strength, if available.
-        if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) {
-            final int newSignal = newNc.getSignalStrength();
-            final String prevSignal = getSignalStrength(prev.networkCapabilities);
-            mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
-        }
-
         mNetworkMap.put(network, new UpstreamNetworkState(
                 prev.linkProperties, newNc, network));
         // TODO: If sufficient information is available to select a more
@@ -557,11 +550,6 @@
         return prefixSet;
     }
 
-    private static String getSignalStrength(NetworkCapabilities nc) {
-        if (nc == null || !nc.hasSignalStrength()) return "unknown";
-        return Integer.toString(nc.getSignalStrength());
-    }
-
     private static boolean isCellular(UpstreamNetworkState ns) {
         return (ns != null) && isCellular(ns.networkCapabilities);
     }
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 81a0548..53782fed 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -20,7 +20,11 @@
     srcs: [
         "src/**/*.java",
     ],
-    test_suites: ["device-tests"],
+    test_suites: [
+        "device-tests",
+        "mts",
+    ],
+    compile_multilib: "both",
     static_libs: [
         "androidx.test.rules",
         "frameworks-base-testutils",
diff --git a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
index e01ac7f..e8add98 100644
--- a/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -18,8 +18,6 @@
 
 import static android.net.InetAddresses.parseNumericAddress;
 
-import static com.google.android.collect.Sets.newHashSet;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -34,6 +32,8 @@
 import org.junit.runner.RunWith;
 
 import java.net.Inet4Address;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -47,9 +47,10 @@
     private static final int TEST_LEASE_TIME_SECS = 120;
     private static final int TEST_MTU = 1000;
     private static final Set<Inet4Address> TEST_ADDRESS_SET =
-            newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124"));
+            new HashSet<Inet4Address>(Arrays.asList(
+            new Inet4Address[] {inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124")}));
     private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
-            newHashSet(0xc0a8017b, 0xc0a8017c);
+            new HashSet<Integer>(Arrays.asList(new Integer[] {0xc0a8017b, 0xc0a8017c}));
 
     private DhcpServingParamsParcelExt mParcel;
 
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 4358cd6..fd2f708 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -510,8 +510,10 @@
         }
         assertNotNull("missing IPv4 address", addr4);
 
+        final IpPrefix destination = new IpPrefix(addr4.getAddress(), addr4.getPrefixLength());
         // Assert the presence of the associated directly connected route.
-        final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
+        final RouteInfo directlyConnected = new RouteInfo(destination, null, lp.getInterfaceName(),
+                RouteInfo.RTN_UNICAST);
         assertTrue("missing directly connected route: '" + directlyConnected.toString() + "'",
                    lp.getRoutes().contains(directlyConnected));
     }
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 8574f54..7886ca6 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -21,6 +21,7 @@
 import static android.net.NetworkStats.STATS_PER_UID;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
+import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.TrafficStats.UID_TETHERING;
 import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
 
@@ -269,7 +270,7 @@
         final String ipv4Addr = "192.0.2.5";
         final String linkAddr = ipv4Addr + "/24";
         lp.addLinkAddress(new LinkAddress(linkAddr));
-        lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24")));
+        lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, null, RTN_UNICAST));
         offload.setUpstreamLinkProperties(lp);
         // IPv4 prefixes and addresses on the upstream are simply left as whole
         // prefixes (already passed in from UpstreamNetworkMonitor code). If a
@@ -285,7 +286,7 @@
         inOrder.verifyNoMoreInteractions();
 
         final String ipv4Gateway = "192.0.2.1";
-        lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway)));
+        lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv4Gateway), null, RTN_UNICAST));
         offload.setUpstreamLinkProperties(lp);
         // No change in local addresses means no call to setLocalPrefixes().
         inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -296,7 +297,7 @@
         inOrder.verifyNoMoreInteractions();
 
         final String ipv6Gw1 = "fe80::cafe";
-        lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1)));
+        lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw1), null, RTN_UNICAST));
         offload.setUpstreamLinkProperties(lp);
         // No change in local addresses means no call to setLocalPrefixes().
         inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -310,7 +311,7 @@
         inOrder.verifyNoMoreInteractions();
 
         final String ipv6Gw2 = "fe80::d00d";
-        lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2)));
+        lp.addRoute(new RouteInfo(null, InetAddress.getByName(ipv6Gw2), null, RTN_UNICAST));
         offload.setUpstreamLinkProperties(lp);
         // No change in local addresses means no call to setLocalPrefixes().
         inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
@@ -327,8 +328,10 @@
         final LinkProperties stacked = new LinkProperties();
         stacked.setInterfaceName("stacked");
         stacked.addLinkAddress(new LinkAddress("192.0.2.129/25"));
-        stacked.addRoute(new RouteInfo(InetAddress.getByName("192.0.2.254")));
-        stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00")));
+        stacked.addRoute(new RouteInfo(null, InetAddress.getByName("192.0.2.254"), null,
+                  RTN_UNICAST));
+        stacked.addRoute(new RouteInfo(null, InetAddress.getByName("fe80::bad:f00"), null,
+                  RTN_UNICAST));
         assertTrue(lp.addStackedLink(stacked));
         offload.setUpstreamLinkProperties(lp);
         // No change in local addresses means no call to setLocalPrefixes().
@@ -348,7 +351,7 @@
         // removed from "local prefixes" and /128s added for the upstream IPv6
         // addresses.  This is not yet implemented, and for now we simply
         // expect to see these /128s.
-        lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64")));
+        lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null, RTN_UNICAST));
         // "2001:db8::/64" plus "assigned" ASCII in hex
         lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64"));
         // "2001:db8::/64" plus "random" ASCII in hex
@@ -574,13 +577,15 @@
         final LinkProperties usbLinkProperties = new LinkProperties();
         usbLinkProperties.setInterfaceName(RNDIS0);
         usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
-        usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX)));
+        usbLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
         offload.notifyDownstreamLinkProperties(usbLinkProperties);
         inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX);
         inOrder.verifyNoMoreInteractions();
 
         // [2] Routes for IPv6 link-local prefixes should never be added.
-        usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL)));
+        usbLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
         offload.notifyDownstreamLinkProperties(usbLinkProperties);
         inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString());
         inOrder.verifyNoMoreInteractions();
@@ -588,7 +593,8 @@
         // [3] Add an IPv6 prefix for good measure. Only new offload-able
         // prefixes should be passed to the HAL.
         usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64"));
-        usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX)));
+        usbLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST));
         offload.notifyDownstreamLinkProperties(usbLinkProperties);
         inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
         inOrder.verifyNoMoreInteractions();
@@ -601,8 +607,10 @@
 
         // [5] Differences in local routes are converted into addDownstream()
         // and removeDownstream() invocations accordingly.
-        usbLinkProperties.removeRoute(new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0));
-        usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX)));
+        usbLinkProperties.removeRoute(
+                new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, RNDIS0, RTN_UNICAST));
+        usbLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST));
         offload.notifyDownstreamLinkProperties(usbLinkProperties);
         inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX);
         inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX);
@@ -680,19 +688,23 @@
         final LinkProperties usbLinkProperties = new LinkProperties();
         usbLinkProperties.setInterfaceName(RNDIS0);
         usbLinkProperties.addLinkAddress(new LinkAddress("192.168.42.1/24"));
-        usbLinkProperties.addRoute(new RouteInfo(new IpPrefix(USB_PREFIX)));
+        usbLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST));
         offload.notifyDownstreamLinkProperties(usbLinkProperties);
 
         final LinkProperties wifiLinkProperties = new LinkProperties();
         wifiLinkProperties.setInterfaceName(WLAN0);
         wifiLinkProperties.addLinkAddress(new LinkAddress("192.168.43.1/24"));
-        wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(WIFI_PREFIX)));
-        wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix(IPV6_LINKLOCAL)));
+        wifiLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(WIFI_PREFIX), null, null, RTN_UNICAST));
+        wifiLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST));
         // Use a benchmark prefix (RFC 5180 + erratum), since the documentation
         // prefix is included in the excluded prefix list.
         wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::1/64"));
         wifiLinkProperties.addLinkAddress(new LinkAddress("2001:2::2/64"));
-        wifiLinkProperties.addRoute(new RouteInfo(new IpPrefix("2001:2::/64")));
+        wifiLinkProperties.addRoute(
+                new RouteInfo(new IpPrefix("2001:2::/64"), null, null, RTN_UNICAST));
         offload.notifyDownstreamLinkProperties(wifiLinkProperties);
 
         offload.removeDownstreamInterface(RNDIS0);
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 30bff35..7799da4 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -34,7 +34,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
@@ -145,7 +144,7 @@
 
     @Test
     public void testDunFromTelephonyManagerMeansDun() {
-        when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(true);
+        when(mTelephonyManager.isTetheringApnRequired()).thenReturn(true);
 
         final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
         final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
@@ -169,7 +168,7 @@
 
     @Test
     public void testDunNotRequiredFromTelephonyManagerMeansNoDun() {
-        when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+        when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
         final TetheringConfiguration cfgWifi = getTetheringConfiguration(TYPE_WIFI);
         final TetheringConfiguration cfgMobileWifiHipri = getTetheringConfiguration(
@@ -212,7 +211,7 @@
     @Test
     public void testNoDefinedUpstreamTypesAddsEthernet() {
         when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{});
-        when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+        when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
         final TetheringConfiguration cfg = new TetheringConfiguration(
                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -235,7 +234,7 @@
     public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
         when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
                 new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
-        when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+        when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
         final TetheringConfiguration cfg = new TetheringConfiguration(
                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
@@ -253,7 +252,7 @@
     public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
         when(mResources.getIntArray(config_tether_upstream_types))
                 .thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
-        when(mTelephonyManager.isTetheringApnRequired(anyInt())).thenReturn(false);
+        when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
 
         final TetheringConfiguration cfg = new TetheringConfiguration(
                 mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
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 9d9ad10..04b2eb4 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
@@ -28,6 +28,7 @@
 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
 import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
+import static android.net.RouteInfo.RTN_UNICAST;
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -72,6 +73,7 @@
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.ITetheringEventCallback;
+import android.net.InetAddresses;
 import android.net.InterfaceConfiguration;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -81,7 +83,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
-import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.TetherStatesParcel;
 import android.net.TetheringConfigurationParcel;
@@ -365,23 +366,26 @@
 
         if (withIPv4) {
             prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
-                    NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
+                    InetAddresses.parseNumericAddress("10.0.0.1"),
+                    TEST_MOBILE_IFNAME, RTN_UNICAST));
         }
 
         if (withIPv6) {
-            prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
+            prop.addDnsServer(InetAddresses.parseNumericAddress("2001:db8::2"));
             prop.addLinkAddress(
-                    new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
+                    new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::"),
                             NetworkConstants.RFC7421_PREFIX_LENGTH));
             prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
-                    NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
+                    InetAddresses.parseNumericAddress("2001:db8::1"),
+                    TEST_MOBILE_IFNAME, RTN_UNICAST));
         }
 
         if (with464xlat) {
             final LinkProperties stackedLink = new LinkProperties();
             stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
             stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
-                    NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+                    InetAddresses.parseNumericAddress("192.0.0.1"),
+                    TEST_XLAT_MOBILE_IFNAME, RTN_UNICAST));
 
             prop.addStackedLink(stackedLink);
         }
@@ -1210,12 +1214,12 @@
     @Test
     public void testMultiSimAware() throws Exception {
         final TetheringConfiguration initailConfig = mTethering.getTetheringConfiguration();
-        assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.subId);
+        assertEquals(INVALID_SUBSCRIPTION_ID, initailConfig.activeDataSubId);
 
         final int fakeSubId = 1234;
         mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
         final TetheringConfiguration newConfig = mTethering.getTetheringConfiguration();
-        assertEquals(fakeSubId, newConfig.subId);
+        assertEquals(fakeSubId, newConfig.activeDataSubId);
     }
 
     private void workingWifiP2pGroupOwner(
diff --git a/services/Android.bp b/services/Android.bp
index ede4c3e..1e11936 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -61,6 +61,7 @@
         "services.devicepolicy",
         "services.midi",
         "services.net",
+        "services.people",
         "services.print",
         "services.restrictions",
         "services.startop",
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
index 6a1f1a5..a0428a5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiTapAndHold.java
@@ -38,6 +38,12 @@
     }
 
     @Override
+    protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        super.onUp(event, rawEvent, policyFlags);
+        cancelAfterDoubleTapTimeout(event, rawEvent, policyFlags);
+    }
+
+    @Override
     public String getGestureName() {
         switch (mTargetTaps) {
             case 2:
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index f6eb31b..ba890c5 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -286,10 +286,6 @@
 
     @Override
     public void onDoubleTapAndHold() {
-        // Pointers should not be zero when running this command.
-        if (mState.getLastReceivedEvent().getPointerCount() == 0) {
-            return;
-        }
         // Try to use the standard accessibility API to long click
         if (!mAms.performActionOnAccessibilityFocusedItem(
                 AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index c689ed1..03d9626 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -288,7 +288,14 @@
             boolean isTemporary) {
         mAugmentedAutofillState.setServiceInfo(userId, serviceName, isTemporary);
         synchronized (mLock) {
-            getServiceForUserLocked(userId).updateRemoteAugmentedAutofillService();
+            final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+            if (service == null) {
+                // If we cannot get the service from the services cache, it will call
+                // updateRemoteAugmentedAutofillService() finally. Skip call this update again.
+                getServiceForUserLocked(userId);
+            } else {
+                service.updateRemoteAugmentedAutofillService();
+            }
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 202f900..bf801fc 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -970,6 +970,8 @@
         } else {
             pw.println(compatPkgs);
         }
+        pw.print(prefix); pw.print("Inline Suggestions Enabled: ");
+        pw.println(isInlineSuggestionsEnabled());
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
         pw.print(prefix); pw.print("Disabled apps: ");
@@ -1120,6 +1122,14 @@
     }
 
     @GuardedBy("mLock")
+    boolean isInlineSuggestionsEnabled() {
+        if (mInfo != null) {
+            return mInfo.isInlineSuggestionsEnabled();
+        }
+        return false;
+    }
+
+    @GuardedBy("mLock")
     @Nullable RemoteAugmentedAutofillService getRemoteAugmentedAutofillServiceLocked() {
         if (mRemoteAugmentedAutofillService == null) {
             final String serviceName = mMaster.mAugmentedAutofillResolver.getServiceName(mUserId);
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
new file mode 100644
index 0000000..3af15c7
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
@@ -0,0 +1,135 @@
+/*
+ * 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.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.service.autofill.Dataset;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.autofill.AutofillId;
+import android.view.autofill.IAutoFillManagerClient;
+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 com.android.internal.view.inline.IInlineContentCallback;
+import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.autofill.ui.InlineSuggestionUi;
+
+import java.util.ArrayList;
+
+
+/**
+ * @hide
+ */
+public final class InlineSuggestionFactory {
+    private static final String TAG = "InlineSuggestionFactory";
+
+    /**
+     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
+     * augmented autofill service.
+     */
+    public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
+            int sessionId,
+            @NonNull Dataset[] datasets,
+            @NonNull AutofillId autofillId,
+            @NonNull InlineSuggestionsRequest request,
+            @NonNull Handler uiHandler,
+            @NonNull Context context,
+            @NonNull IAutoFillManagerClient client) {
+        if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+
+        final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
+        final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context);
+        for (Dataset dataset : datasets) {
+            // TODO(b/146453195): use the spec in the dataset.
+            InlinePresentationSpec spec = request.getPresentationSpecs().get(0);
+            if (spec == null) {
+                Slog.w(TAG, "InlinePresentationSpec is not provided in the response data set");
+                continue;
+            }
+            InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(sessionId, dataset,
+                    autofillId, spec, uiHandler, inlineSuggestionUi, client);
+            inlineSuggestions.add(inlineSuggestion);
+        }
+        return new InlineSuggestionsResponse(inlineSuggestions);
+    }
+
+    private static InlineSuggestion createAugmentedInlineSuggestion(int sessionId,
+            @NonNull Dataset dataset,
+            @NonNull AutofillId autofillId,
+            @NonNull InlinePresentationSpec spec,
+            @NonNull Handler uiHandler,
+            @NonNull InlineSuggestionUi inlineSuggestionUi,
+            @NonNull IAutoFillManagerClient client) {
+        // TODO(b/146453195): fill in the autofill hint properly.
+        final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
+                spec, InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""});
+        final View.OnClickListener onClickListener = createOnClickListener(sessionId, dataset,
+                client);
+        final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
+                createInlineContentProvider(autofillId, dataset, uiHandler, inlineSuggestionUi,
+                        onClickListener));
+        return inlineSuggestion;
+    }
+
+    private static IInlineContentProvider.Stub createInlineContentProvider(
+            @NonNull AutofillId autofillId, @NonNull Dataset dataset, @NonNull Handler uiHandler,
+            @NonNull InlineSuggestionUi inlineSuggestionUi,
+            @Nullable View.OnClickListener onClickListener) {
+        return new IInlineContentProvider.Stub() {
+            @Override
+            public void provideContent(int width, int height,
+                    IInlineContentCallback callback) {
+                uiHandler.post(() -> {
+                    SurfaceControl sc = inlineSuggestionUi.inflate(dataset, autofillId,
+                            width, height, onClickListener);
+                    try {
+                        callback.onContent(sc);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Encounter exception calling back with inline content.");
+                    }
+                });
+            }
+        };
+    }
+
+    private static View.OnClickListener createOnClickListener(int sessionId,
+            @NonNull Dataset dataset,
+            @NonNull IAutoFillManagerClient client) {
+        return v -> {
+            if (sDebug) Slog.d(TAG, "Inline suggestion clicked");
+            try {
+                client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues());
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Encounter exception autofilling the values");
+            }
+        };
+    }
+
+    private InlineSuggestionFactory() {
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index c5011b3..2a7357b 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -46,12 +46,15 @@
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
+import android.view.inputmethod.InlineSuggestionsRequest;
 
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.infra.ServiceConnector;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
 
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.TimeUnit;
@@ -137,7 +140,9 @@
      */
     public void onRequestAutofillLocked(int sessionId, @NonNull IAutoFillManagerClient client,
             int taskId, @NonNull ComponentName activityComponent, @NonNull AutofillId focusedId,
-            @Nullable AutofillValue focusedValue) {
+            @Nullable AutofillValue focusedValue,
+            @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+            @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback) {
         long requestTime = SystemClock.elapsedRealtime();
         AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
 
@@ -151,10 +156,13 @@
                     final IBinder realClient = resultData
                             .getBinder(AutofillManager.EXTRA_AUGMENTED_AUTOFILL_CLIENT);
                     service.onFillRequest(sessionId, realClient, taskId, activityComponent,
-                            focusedId, focusedValue, requestTime, new IFillCallback.Stub() {
+                            focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
+                            new IFillCallback.Stub() {
                                 @Override
                                 public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
-                                    // TODO(b/146453195): handle non-null inline suggestions data.
+                                    maybeHandleInlineSuggestions(sessionId, inlineSuggestionsData,
+                                            focusedId, inlineSuggestionsRequest,
+                                            inlineSuggestionsCallback, client);
                                     requestAutofill.complete(null);
                                 }
 
@@ -216,6 +224,26 @@
         });
     }
 
+    private void maybeHandleInlineSuggestions(int sessionId,
+            @Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
+            @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
+            @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
+            @NonNull IAutoFillManagerClient client) {
+        if (inlineSuggestionsRequest == null
+                || ArrayUtils.isEmpty(inlineSuggestionsData)
+                || inlineSuggestionsCallback == null) {
+            return;
+        }
+        try {
+            inlineSuggestionsCallback.onInlineSuggestionsResponse(
+                    InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
+                            sessionId, inlineSuggestionsData, focusedId, inlineSuggestionsRequest,
+                            getJobHandler(), mContext, client));
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
+        }
+    }
+
     @Override
     public String toString() {
         return "RemoteAugmentedAutofillService["
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5af4399..95cd8fc 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -42,8 +42,6 @@
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.AutofillOverlay;
 import android.app.assist.AssistStructure.ViewNode;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -72,6 +70,7 @@
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
+import android.service.autofill.InlinePresentation;
 import android.service.autofill.InternalSanitizer;
 import android.service.autofill.InternalValidator;
 import android.service.autofill.SaveInfo;
@@ -83,7 +82,6 @@
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
-import android.util.Size;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -323,7 +321,7 @@
             mInlineSuggestionsResponseCallbackFuture;
 
     @Nullable
-    private InlineSuggestionsRequestCallback mInlineSuggestionsRequestCallback;
+    private InlineSuggestionsRequestCallbackImpl mInlineSuggestionsRequestCallback;
 
     private static final int INLINE_REQUEST_TIMEOUT_MS = 1000;
 
@@ -424,19 +422,7 @@
                 final ArrayList<FillContext> contexts =
                         mergePreviousSessionLocked(/* forSave= */ false);
 
-                InlineSuggestionsRequest suggestionsRequest = null;
-                if (mSuggestionsRequestFuture != null) {
-                    try {
-                        suggestionsRequest = mSuggestionsRequestFuture.get(
-                                INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-                    } catch (TimeoutException e) {
-                        Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
-                    } catch (CancellationException e) {
-                        Log.w(TAG, "Inline suggestions request cancelled");
-                    } catch (InterruptedException | ExecutionException e) {
-                        throw new RuntimeException(e);
-                    }
-                }
+                final InlineSuggestionsRequest suggestionsRequest = getInlineSuggestionsRequest();
 
                 request = new FillRequest(requestId, contexts, mClientState, flags,
                         suggestionsRequest);
@@ -624,9 +610,8 @@
     /**
      * Returns whether inline suggestions are enabled for Autofill.
      */
-    // TODO(b/137800469): Implement this
     private boolean isInlineSuggestionsEnabled() {
-        return true;
+        return mService.isInlineSuggestionsEnabled();
     }
 
     /**
@@ -639,7 +624,7 @@
             mInlineSuggestionsResponseCallbackFuture = new CompletableFuture<>();
 
             if (mInlineSuggestionsRequestCallback == null) {
-                mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallback(this);
+                mInlineSuggestionsRequestCallback = new InlineSuggestionsRequestCallbackImpl(this);
             }
 
             mInputMethodManagerInternal.onCreateInlineSuggestionsRequest(
@@ -649,18 +634,20 @@
         requestNewFillResponseLocked(viewState, newState, flags);
     }
 
-    private static final class InlineSuggestionsRequestCallback
+    private static final class InlineSuggestionsRequestCallbackImpl
             extends IInlineSuggestionsRequestCallback.Stub {
         private final WeakReference<Session> mSession;
 
-        private InlineSuggestionsRequestCallback(Session session) {
+        private InlineSuggestionsRequestCallbackImpl(Session session) {
             mSession = new WeakReference<>(session);
         }
 
         @Override
         public void onInlineSuggestionsUnsupported() throws RemoteException {
-            Log.i(TAG, "inline suggestions request unsupported, "
-                    + "falling back to regular autofill");
+            if (sDebug) {
+                Log.d(TAG, "inline suggestions request unsupported, "
+                        + "falling back to regular autofill");
+            }
             final Session session = mSession.get();
             if (session != null) {
                 synchronized (session.mLock) {
@@ -673,8 +660,9 @@
         @Override
         public void onInlineSuggestionsRequest(InlineSuggestionsRequest request,
                 IInlineSuggestionsResponseCallback callback) throws RemoteException {
-            Log.i(TAG, "onInlineSuggestionsRequest() received: "
-                    + request);
+            if (sDebug) {
+                Log.d(TAG, "onInlineSuggestionsRequest() received: " + request);
+            }
             final Session session = mSession.get();
             if (session != null) {
                 synchronized (session.mLock) {
@@ -2647,9 +2635,8 @@
             return;
         }
 
-        final List<Slice> inlineSuggestionSlices = response.getInlineSuggestionSlices();
-        if (inlineSuggestionSlices != null) {
-            if (requestShowInlineSuggestions(inlineSuggestionSlices, response)) {
+        if (response.supportsInlineSuggestions()) {
+            if (requestShowInlineSuggestions(response)) {
                 //TODO(b/137800469): Add logging instead of bypassing below logic.
                 return;
             }
@@ -2690,8 +2677,7 @@
     /**
      * Returns whether we made a request to show inline suggestions.
      */
-    private boolean requestShowInlineSuggestions(List<Slice> inlineSuggestionSlices,
-            FillResponse response) {
+    private boolean requestShowInlineSuggestions(FillResponse response) {
         IInlineSuggestionsResponseCallback inlineContentCallback = null;
         synchronized (mLock) {
             if (mInlineSuggestionsResponseCallbackFuture != null) {
@@ -2719,54 +2705,20 @@
             return false;
         }
 
+        final int size = datasets.size();
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
-        final int slicesSize = inlineSuggestionSlices.size();
-        if (datasets.size() < slicesSize) {
-            Log.w(TAG, "Too many slices provided, not enough corresponding datasets");
-            return false;
-        }
 
-        for (int sliceIndex = 0; sliceIndex < slicesSize; sliceIndex++) {
-            Log.i(TAG, "Reading slice-" + sliceIndex + " at requestshowinlinesuggestions");
-            final Slice inlineSuggestionSlice = inlineSuggestionSlices.get(sliceIndex);
-            final List<SliceItem> sliceItems = inlineSuggestionSlice.getItems();
-
-            final int itemsSize = sliceItems.size();
-            int minWidth = -1;
-            int maxWidth = -1;
-            int minHeight = -1;
-            int maxHeight = -1;
-            for (int itemIndex = 0; itemIndex < itemsSize; itemIndex++) {
-                final SliceItem item = sliceItems.get(itemIndex);
-                final String subtype = item.getSubType();
-                switch (item.getSubType()) {
-                    case "SUBTYPE_MIN_WIDTH":
-                        minWidth = item.getInt();
-                        break;
-                    case "SUBTYPE_MAX_WIDTH":
-                        maxWidth = item.getInt();
-                        break;
-                    case "SUBTYPE_MIN_HEIGHT":
-                        minHeight = item.getInt();
-                        break;
-                    case "SUBTYPE_MAX_HEIGHT":
-                        maxHeight = item.getInt();
-                        break;
-                    default:
-                        Log.i(TAG, "unrecognized inline suggestions subtype: " + subtype);
-                }
+        for (int index = 0; index < size; index++) {
+            final Dataset dataset = datasets.get(index);
+            //TODO(b/146453536): Use the proper presentation/spec for currently focused view.
+            final InlinePresentation inlinePresentation = dataset.getFieldInlinePresentation(0);
+            if (inlinePresentation == null) {
+                if (sDebug) Log.d(TAG, "Missing InlinePresentation on dataset=" + dataset);
+                continue;
             }
-
-            if (minWidth < 0 || maxWidth < 0 || minHeight < 0 || maxHeight < 0) {
-                Log.w(TAG, "missing inline suggestion requirements");
-                return false;
-            }
-
-            final InlinePresentationSpec spec = new InlinePresentationSpec.Builder(
-                    new Size(minWidth, minHeight), new Size(maxWidth, maxHeight)).build();
+            final InlinePresentationSpec spec = inlinePresentation.getInlinePresentationSpec();
             final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                     spec, InlineSuggestionInfo.SOURCE_AUTOFILL, new String[] { "" });
-            final Dataset dataset = datasets.get(sliceIndex);
 
             inlineSuggestions.add(new InlineSuggestion(inlineSuggestionInfo,
                     new IInlineContentProvider.Stub() {
@@ -3063,9 +3015,12 @@
 
         final AutofillId focusedId = AutofillId.withoutSession(mCurrentViewId);
 
-        // TODO(b/137800469): implement inlined suggestions for augmented autofill
+        final InlineSuggestionsRequest inlineSuggestionsRequest = getInlineSuggestionsRequest();
+        final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
+                getInlineSuggestionsResponseCallback();
+
         remoteService.onRequestAutofillLocked(id, mClient, taskId, mComponentName, focusedId,
-                currentValue);
+                currentValue, inlineSuggestionsRequest, inlineSuggestionsResponseCallback);
 
         if (mAugmentedAutofillDestroyer == null) {
             mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
@@ -3073,6 +3028,40 @@
         return mAugmentedAutofillDestroyer;
     }
 
+    @Nullable
+    private InlineSuggestionsRequest getInlineSuggestionsRequest() {
+        if (mSuggestionsRequestFuture != null) {
+            try {
+                return mSuggestionsRequestFuture.get(
+                        INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                Log.w(TAG, "Exception getting inline suggestions request in time: " + e);
+            } catch (CancellationException e) {
+                Log.w(TAG, "Inline suggestions request cancelled");
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private IInlineSuggestionsResponseCallback getInlineSuggestionsResponseCallback() {
+        if (mInlineSuggestionsResponseCallbackFuture != null) {
+            try {
+                return mInlineSuggestionsResponseCallbackFuture.get(
+                        INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (TimeoutException e) {
+                Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
+            } catch (CancellationException e) {
+                Log.w(TAG, "Inline suggestions callback cancelled");
+            } catch (InterruptedException | ExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return null;
+    }
+
     @GuardedBy("mLock")
     private void cancelAugmentedAutofillLocked() {
         final RemoteAugmentedAutofillService remoteService = mService
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index e57b7b3..0511bf2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -24,8 +24,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.os.Bundle;
@@ -40,13 +38,9 @@
 import android.util.Slog;
 import android.view.KeyEvent;
 import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.WindowManager;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
-import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutofillWindowPresenter;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import com.android.internal.logging.MetricsLogger;
@@ -189,8 +183,16 @@
             Slog.w(TAG, "getSuggestionSurfaceForShowing() called with null dataset");
         }
         mHandler.post(() -> {
-            final SurfaceControl suggestionSurface = inflateInlineSuggestion(dataset, response,
-                    autofillId, width, height);
+            final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(mContext);
+            final SurfaceControl suggestionSurface = inlineSuggestionUi.inflate(dataset,
+                    autofillId, width, height, v -> {
+                        Slog.d(TAG, "Inline suggestion clicked");
+                        hideFillUiUiThread(mCallback, true);
+                        if (mCallback != null) {
+                            final int datasetIndex = response.getDatasets().indexOf(dataset);
+                            mCallback.fill(response.getRequestId(), datasetIndex, dataset);
+                        }
+                    });
 
             try {
                 cb.onContent(suggestionSurface);
@@ -201,51 +203,6 @@
     }
 
     /**
-     * TODO(b/137800469): Fill in javadoc, generate custom templated view for inline suggestions.
-     * TODO: Move to ExtServices.
-     *
-     * @return a {@link SurfaceControl} with the inflated content embedded in it.
-     */
-    private SurfaceControl inflateInlineSuggestion(@NonNull Dataset dataset,
-            @NonNull FillResponse response, AutofillId autofillId, int width, int height) {
-        Slog.i(TAG, "inflate() called");
-        final Context context = mContext;
-        final int index = dataset.getFieldIds().indexOf(autofillId);
-        if (index < 0) {
-            Slog.w(TAG, "inflateInlineSuggestion(): AutofillId=" + autofillId
-                    + " not found in dataset");
-        }
-
-        final AutofillValue datasetValue = dataset.getFieldValues().get(index);
-        //TODO(b/137800469): Pass in inputToken from IME.
-        final SurfaceControlViewHost wvr = new SurfaceControlViewHost(context, context.getDisplay(),
-                (IBinder) null);
-        // TODO(b/134365580): Use the package instead of the SurfaceControl itself
-        // for accessibility support.
-        final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
-
-        TextView textView = new TextView(context);
-        textView.setText(datasetValue.getTextValue());
-        textView.setBackgroundColor(Color.WHITE);
-        textView.setTextColor(Color.BLACK);
-        textView.setOnClickListener(v -> {
-            Slog.d(TAG, "Inline suggestion clicked");
-            hideFillUiUiThread(mCallback, true);
-            if (mCallback != null) {
-                final int datasetIndex = response.getDatasets().indexOf(dataset);
-                mCallback.fill(response.getRequestId(), datasetIndex, dataset);
-            }
-        });
-
-        WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(width, height,
-                        WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
-        wvr.addView(textView, lp);
-
-        return sc;
-    }
-
-    /**
      * Shows the fill UI, removing the previous fill UI if the has changed.
      *
      * @param focusedId the currently focused field
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
new file mode 100644
index 0000000..17cb739
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.service.autofill.Dataset;
+import android.util.Log;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillValue;
+import android.widget.TextView;
+
+/**
+ * This is a temporary inline suggestion UI inflater which will be replaced by the ExtServices
+ * implementation.
+ *
+ * TODO(b/146453086): remove this class once autofill ext service is implemented.
+ *
+ * @hide
+ */
+public class InlineSuggestionUi {
+
+    private static final String TAG = "InlineSuggestionUi";
+
+    private final Context mContext;
+
+    public InlineSuggestionUi(Context context) {
+        this.mContext = context;
+    }
+
+    /**
+     * Returns a {@link SurfaceControl} with the inflated content embedded in it.
+     */
+    @MainThread
+    @Nullable
+    public SurfaceControl inflate(@NonNull Dataset dataset, @NonNull AutofillId autofillId,
+            int width, int height, @Nullable View.OnClickListener onClickListener) {
+        Log.d(TAG, "Inflating the inline suggestion UI");
+        final int index = dataset.getFieldIds().indexOf(autofillId);
+        if (index < 0) {
+            Slog.w(TAG, "inflateInlineSuggestion(): AutofillId=" + autofillId
+                    + " not found in dataset");
+            return null;
+        }
+        final AutofillValue datasetValue = dataset.getFieldValues().get(index);
+        //TODO(b/137800469): Pass in inputToken from IME.
+        final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext,
+                mContext.getDisplay(), (IBinder) null);
+        final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
+
+        TextView textView = new TextView(mContext);
+        textView.setText(datasetValue.getTextValue());
+        textView.setBackgroundColor(Color.WHITE);
+        textView.setTextColor(Color.BLACK);
+        if (onClickListener != null) {
+            textView.setOnClickListener(onClickListener);
+        }
+
+        WindowManager.LayoutParams lp =
+                new WindowManager.LayoutParams(width, height,
+                        WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+        wvr.addView(textView, lp);
+        return sc;
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 3651a41..3bce322 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -16,8 +16,6 @@
 
 package com.android.server.backup;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import static java.util.Collections.emptySet;
 
 import android.Manifest;
@@ -68,6 +66,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -124,7 +123,7 @@
     static BackupManagerService sInstance;
 
     static BackupManagerService getInstance() {
-        return checkNotNull(sInstance);
+        return Objects.requireNonNull(sInstance);
     }
 
     private final Context mContext;
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 92c2ee4..c9b09e3 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -21,7 +21,6 @@
 import android.os.SELinux;
 import android.util.Slog;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.backup.fullbackup.AppMetadataBackupWriter;
 import com.android.server.backup.remote.ServiceBackupCallback;
 import com.android.server.backup.utils.FullBackupUtils;
@@ -33,6 +32,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Objects;
 
 /**
  * Used by BackupManagerService to perform adb backup for key-value packages. At the moment this
@@ -87,7 +87,7 @@
                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
 
         mManifestFile = new File(mDataDir, BACKUP_MANIFEST_FILENAME);
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
     }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2799f12..064cd06 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -18,7 +18,6 @@
 
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
@@ -159,6 +158,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Queue;
 import java.util.Random;
 import java.util.Set;
@@ -518,7 +518,7 @@
             File dataDir,
             TransportManager transportManager) {
         mUserId = userId;
-        mContext = checkNotNull(context, "context cannot be null");
+        mContext = Objects.requireNonNull(context, "context cannot be null");
         mPackageManager = context.getPackageManager();
         mPackageManagerBinder = AppGlobals.getPackageManager();
         mActivityManager = ActivityManager.getService();
@@ -528,14 +528,14 @@
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
 
-        checkNotNull(parent, "parent cannot be null");
+        Objects.requireNonNull(parent, "parent cannot be null");
         mBackupManagerBinder = BackupManagerService.asInterface(parent.asBinder());
 
         mAgentTimeoutParameters = new
                 BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
         mAgentTimeoutParameters.start();
 
-        checkNotNull(userBackupThread, "userBackupThread cannot be null");
+        Objects.requireNonNull(userBackupThread, "userBackupThread cannot be null");
         mBackupHandler = new BackupHandler(this, userBackupThread);
 
         // Set up our bookkeeping
@@ -551,7 +551,7 @@
                 mSetupObserver,
                 mUserId);
 
-        mBaseStateDir = checkNotNull(baseStateDir, "baseStateDir cannot be null");
+        mBaseStateDir = Objects.requireNonNull(baseStateDir, "baseStateDir cannot be null");
         // TODO (b/120424138): Remove once the system user is migrated to use the per-user CE
         // directory. Per-user CE directories are managed by vold.
         if (userId == UserHandle.USER_SYSTEM) {
@@ -563,7 +563,7 @@
 
         // TODO (b/120424138): The system user currently uses the cache which is managed by init.rc
         // Initialization and restorecon is managed by vold for per-user CE directories.
-        mDataDir = checkNotNull(dataDir, "dataDir cannot be null");
+        mDataDir = Objects.requireNonNull(dataDir, "dataDir cannot be null");
         mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
 
         // Receivers for scheduled backups and transport initialization operations.
@@ -625,7 +625,7 @@
             addPackageParticipantsLocked(null);
         }
 
-        mTransportManager = checkNotNull(transportManager, "transportManager cannot be null");
+        mTransportManager = Objects.requireNonNull(transportManager, "transportManager cannot be null");
         mTransportManager.setOnTransportRegisteredListener(this::onTransportRegistered);
         mRegisterTransportsRequestedTime = SystemClock.elapsedRealtime();
         mBackupHandler.postDelayed(
@@ -3047,9 +3047,9 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BACKUP, "updateTransportAttributes");
 
-        Preconditions.checkNotNull(transportComponent, "transportComponent can't be null");
-        Preconditions.checkNotNull(name, "name can't be null");
-        Preconditions.checkNotNull(
+        Objects.requireNonNull(transportComponent, "transportComponent can't be null");
+        Objects.requireNonNull(name, "name can't be null");
+        Objects.requireNonNull(
                 currentDestinationString, "currentDestinationString can't be null");
         Preconditions.checkArgument(
                 (dataManagementIntent == null) == (dataManagementLabel == null),
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 7ea1892..846c6a2 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -36,7 +36,6 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
@@ -47,6 +46,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Objects;
 
 /**
  * Core logic for performing one package's full backup, gathering the tarball from the application
@@ -201,7 +201,7 @@
         mOpToken = opToken;
         mTransportFlags = transportFlags;
         mAgentTimeoutParameters =
-                Preconditions.checkNotNull(
+                Objects.requireNonNull(
                         backupManagerService.getAgentTimeoutParameters(),
                         "Timeout parameters cannot be null");
     }
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index e142537..aaf1f0a 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -32,13 +32,13 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IObbBackupService;
-import com.android.internal.util.Preconditions;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.UserBackupManagerService;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Objects;
 
 /**
  * Full backup/restore to a file/socket.
@@ -52,7 +52,7 @@
     public FullBackupObbConnection(UserBackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
         mService = null;
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
     }
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 18c38dc..f7f0138 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -42,7 +42,6 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
@@ -62,6 +61,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
@@ -171,7 +171,7 @@
         mUserInitiated = userInitiated;
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
         mUserId = backupManagerService.getUserId();
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 8c48b84..eb62620 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -31,8 +31,8 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
@@ -58,6 +58,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Asynchronous backup/restore handler thread.
@@ -91,14 +92,16 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     private final HandlerThread mBackupThread;
-    private volatile boolean mIsStopping = false;
+
+    @VisibleForTesting
+    volatile boolean mIsStopping = false;
 
     public BackupHandler(
             UserBackupManagerService backupManagerService, HandlerThread backupThread) {
         super(backupThread.getLooper());
         mBackupThread = backupThread;
         this.backupManagerService = backupManagerService;
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
     }
@@ -113,6 +116,24 @@
         sendMessage(obtainMessage(BackupHandler.MSG_STOP));
     }
 
+    @Override
+    public void dispatchMessage(Message message) {
+        try {
+            dispatchMessageInternal(message);
+        } catch (Exception e) {
+            // If the backup service is stopping, we'll suppress all exceptions to avoid crashes
+            // caused by code still running after the current user has become unavailable.
+            if (!mIsStopping) {
+                throw e;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    void dispatchMessageInternal(Message message) {
+        super.dispatchMessage(message);
+    }
+
     public void handleMessage(Message msg) {
         if (msg.what == MSG_STOP) {
             Slog.v(TAG, "Stopping backup handler");
@@ -414,7 +435,7 @@
                             try {
                                 params.observer.onTimeout();
                             } catch (RemoteException e) {
-                            /* don't care if the app has gone away */
+                                /* don't care if the app has gone away */
                             }
                         }
                     } else {
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 9492118..bda0e3b 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -310,7 +310,7 @@
         mUserInitiated = userInitiated;
         mNonIncremental = nonIncremental;
         mAgentTimeoutParameters =
-                Preconditions.checkNotNull(
+                Objects.requireNonNull(
                         backupManagerService.getAgentTimeoutParameters(),
                         "Timeout parameters cannot be null");
         mStateDirectory = new File(backupManagerService.getBaseStateDir(), transportDirName);
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4890e0..376b618 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -21,11 +21,11 @@
 
 import android.util.Slog;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.UserBackupManagerService;
 
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -45,7 +45,7 @@
         this.backupManagerService = backupManagerService;
         mLatch = new CountDownLatch(1);
         mCurrentOpToken = currentOpToken;
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
     }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index eba9e4a..82bed3b 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -42,7 +42,6 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
@@ -62,6 +61,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Full restore engine, used by both adb restore and transport-based full restore.
@@ -142,7 +142,7 @@
         mOnlyPackage = onlyPackage;
         mAllowApks = allowApks;
         mBuffer = new byte[32 * 1024];
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
         mIsAdbRestore = isAdbRestore;
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index de6a080..16484df9f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -54,7 +54,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.backup.IBackupTransport;
-import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
@@ -79,6 +78,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 public class PerformUnifiedRestoreTask implements BackupRestoreTask {
@@ -208,7 +208,7 @@
         mFinished = false;
         mDidLaunch = false;
         mListener = listener;
-        mAgentTimeoutParameters = Preconditions.checkNotNull(
+        mAgentTimeoutParameters = Objects.requireNonNull(
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5b98f06..a1f57cb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -94,8 +94,8 @@
         "android.hardware.light-V2.0-java",
         "android.hardware.power-V1.0-java",
         "android.hardware.tv.cec-V1.0-java",
+        "android.hardware.vibrator-java",
         "app-compat-annotations",
-        "vintf-vibrator-java",
         "framework-tethering",
     ],
 
@@ -117,6 +117,7 @@
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
+        "android.hardware.rebootescrow-java",
         "android.hardware.soundtrigger-V2.3-java",
         "android.hidl.manager-V1.2-java",
         "dnsresolver_aidl_interface-V2-java",
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 40a7dfc..312dd46 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -319,6 +319,12 @@
             int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
 
     /**
+     * Called by DevicePolicyManagerService to set the package names protected by the device
+     * owner.
+     */
+    public abstract void setDeviceOwnerProtectedPackages(List<String> packageNames);
+
+    /**
      * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
      */
     public abstract boolean isPackageDataProtected(int userId, String packageName);
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index 7d5b176..72e170f 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -63,6 +63,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Class to keep track of the information related to "force app standby", which includes:
@@ -416,12 +417,12 @@
             }
             mStarted = true;
 
-            mIActivityManager = Preconditions.checkNotNull(injectIActivityManager());
-            mActivityManagerInternal = Preconditions.checkNotNull(injectActivityManagerInternal());
-            mAppOpsManager = Preconditions.checkNotNull(injectAppOpsManager());
-            mAppOpsService = Preconditions.checkNotNull(injectIAppOpsService());
-            mPowerManagerInternal = Preconditions.checkNotNull(injectPowerManagerInternal());
-            mAppStandbyInternal = Preconditions.checkNotNull(injectAppStandbyInternal());
+            mIActivityManager = Objects.requireNonNull(injectIActivityManager());
+            mActivityManagerInternal = Objects.requireNonNull(injectActivityManagerInternal());
+            mAppOpsManager = Objects.requireNonNull(injectAppOpsManager());
+            mAppOpsService = Objects.requireNonNull(injectIAppOpsService());
+            mPowerManagerInternal = Objects.requireNonNull(injectPowerManagerInternal());
+            mAppStandbyInternal = Objects.requireNonNull(injectAppStandbyInternal());
 
             mFlagsObserver = new FeatureFlagsObserver();
             mFlagsObserver.register();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bb78ace..bd8a361 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -47,8 +47,6 @@
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IPPROTO_UDP;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.BroadcastOptions;
@@ -904,7 +902,7 @@
          * @see IpConnectivityMetrics.Logger
          */
         public IpConnectivityMetrics.Logger getMetricsLogger() {
-            return checkNotNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
+            return Objects.requireNonNull(LocalServices.getService(IpConnectivityMetrics.Logger.class),
                     "no IpConnectivityMetrics service");
         }
 
@@ -933,7 +931,7 @@
             IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
         if (DBG) log("ConnectivityService starting up");
 
-        mDeps = checkNotNull(deps, "missing Dependencies");
+        mDeps = Objects.requireNonNull(deps, "missing Dependencies");
         mSystemProperties = mDeps.getSystemProperties();
         mNetIdManager = mDeps.makeNetIdManager();
 
@@ -962,14 +960,14 @@
 
         mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
 
-        mContext = checkNotNull(context, "missing Context");
-        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
-        mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
-        mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
-        mPolicyManagerInternal = checkNotNull(
+        mContext = Objects.requireNonNull(context, "missing Context");
+        mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
+        mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
+        mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager");
+        mPolicyManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "missing NetworkPolicyManagerInternal");
-        mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
+        mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
         mProxyTracker = mDeps.makeProxyTracker(mContext, mHandler);
 
         mNetd = netd;
@@ -2025,6 +2023,15 @@
                 NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
     }
 
+    private void enforceAirplaneModePermission() {
+        enforceAnyPermissionOf(
+                android.Manifest.permission.NETWORK_AIRPLANE_MODE,
+                android.Manifest.permission.NETWORK_SETTINGS,
+                android.Manifest.permission.NETWORK_SETUP_WIZARD,
+                android.Manifest.permission.NETWORK_STACK,
+                NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+    }
+
     private boolean checkNetworkStackPermission() {
         return checkAnyPermissionOf(
                 android.Manifest.permission.NETWORK_STACK,
@@ -4736,7 +4743,7 @@
 
     @Override
     public void setAirplaneMode(boolean enable) {
-        enforceNetworkStackSettingsOrSetup();
+        enforceAirplaneModePermission();
         final long ident = Binder.clearCallingIdentity();
         try {
             final ContentResolver cr = mContext.getContentResolver();
@@ -5195,7 +5202,7 @@
     @Override
     public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
             PendingIntent operation) {
-        checkNotNull(operation, "PendingIntent cannot be null.");
+        Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
         enforceNetworkRequestPermissions(networkCapabilities);
         enforceMeteredApnPolicy(networkCapabilities);
@@ -5222,7 +5229,7 @@
 
     @Override
     public void releasePendingNetworkRequest(PendingIntent operation) {
-        checkNotNull(operation, "PendingIntent cannot be null.");
+        Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
                 getCallingUid(), 0, operation));
     }
@@ -5280,7 +5287,7 @@
     @Override
     public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
             PendingIntent operation) {
-        checkNotNull(operation, "PendingIntent cannot be null.");
+        Objects.requireNonNull(operation, "PendingIntent cannot be null.");
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
diff --git a/services/core/java/com/android/server/CountryDetectorService.java b/services/core/java/com/android/server/CountryDetectorService.java
index 861c731..b0132d3 100644
--- a/services/core/java/com/android/server/CountryDetectorService.java
+++ b/services/core/java/com/android/server/CountryDetectorService.java
@@ -24,21 +24,29 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.text.TextUtils;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.server.location.ComprehensiveCountryDetector;
+import com.android.server.location.CountryDetectorBase;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
 import java.util.HashMap;
 
 /**
- * This class detects the country that the user is in through {@link ComprehensiveCountryDetector}.
+ * This class detects the country that the user is in. The default country detection is made through
+ * {@link com.android.server.location.ComprehensiveCountryDetector}. It is possible to overlay the
+ * detection algorithm by overlaying the attribute R.string.config_customCountryDetector with the
+ * custom class name to use instead. The custom class must extend
+ * {@link com.android.server.location.CountryDetectorBase}
  *
  * @hide
  */
@@ -88,7 +96,7 @@
 
     private final HashMap<IBinder, Receiver> mReceivers;
     private final Context mContext;
-    private ComprehensiveCountryDetector mCountryDetector;
+    private CountryDetectorBase mCountryDetector;
     private boolean mSystemReady;
     private Handler mHandler;
     private CountryListener mLocationBasedDetectorListener;
@@ -184,8 +192,17 @@
                 });
     }
 
-    private void initialize() {
-        mCountryDetector = new ComprehensiveCountryDetector(mContext);
+    @VisibleForTesting
+    void initialize() {
+        final String customCountryClass = mContext.getString(R.string.config_customCountryDetector);
+        if (!TextUtils.isEmpty(customCountryClass)) {
+            mCountryDetector = loadCustomCountryDetectorIfAvailable(customCountryClass);
+        }
+
+        if (mCountryDetector == null) {
+            Slog.d(TAG, "Using default country detector");
+            mCountryDetector = new ComprehensiveCountryDetector(mContext);
+        }
         mLocationBasedDetectorListener = country -> mHandler.post(() -> notifyReceivers(country));
     }
 
@@ -194,10 +211,32 @@
     }
 
     @VisibleForTesting
+    CountryDetectorBase getCountryDetector() {
+        return mCountryDetector;
+    }
+
+    @VisibleForTesting
     boolean isSystemReady() {
         return mSystemReady;
     }
 
+    private CountryDetectorBase loadCustomCountryDetectorIfAvailable(
+            final String customCountryClass) {
+        CountryDetectorBase customCountryDetector = null;
+
+        Slog.d(TAG, "Using custom country detector class: " + customCountryClass);
+        try {
+            customCountryDetector = Class.forName(customCountryClass).asSubclass(
+                    CountryDetectorBase.class).getConstructor(Context.class).newInstance(
+                    mContext);
+        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                | NoSuchMethodException | InvocationTargetException e) {
+            Slog.e(TAG, "Could not instantiate the custom country detector class");
+        }
+
+        return customCountryDetector;
+    }
+
     @SuppressWarnings("unused")
     @Override
     protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
@@ -206,9 +245,10 @@
         try {
             final Printer p = new PrintWriterPrinter(fout);
             p.println("CountryDetectorService state:");
+            p.println("Country detector class=" + mCountryDetector.getClass().getName());
             p.println("  Number of listeners=" + mReceivers.keySet().size());
             if (mCountryDetector == null) {
-                p.println("  ComprehensiveCountryDetector not initialized");
+                p.println("  CountryDetector not initialized");
             } else {
                 p.println("  " + mCountryDetector.toString());
             }
diff --git a/services/core/java/com/android/server/ExplicitHealthCheckController.java b/services/core/java/com/android/server/ExplicitHealthCheckController.java
index f7c4aac..77059d9 100644
--- a/services/core/java/com/android/server/ExplicitHealthCheckController.java
+++ b/services/core/java/com/android/server/ExplicitHealthCheckController.java
@@ -47,6 +47,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Consumer;
 
@@ -113,9 +114,9 @@
                 Slog.wtf(TAG, "Resetting health check controller callbacks");
             }
 
-            mPassedConsumer = Preconditions.checkNotNull(passedConsumer);
-            mSupportedConsumer = Preconditions.checkNotNull(supportedConsumer);
-            mNotifySyncRunnable = Preconditions.checkNotNull(notifySyncRunnable);
+            mPassedConsumer = Objects.requireNonNull(passedConsumer);
+            mSupportedConsumer = Objects.requireNonNull(supportedConsumer);
+            mNotifySyncRunnable = Objects.requireNonNull(notifySyncRunnable);
         }
     }
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index a629b3f..c987620 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -25,8 +25,6 @@
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -76,6 +74,7 @@
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A service to manage multiple clients that want to access the IpSec API. The service is
@@ -566,7 +565,7 @@
         }
 
         void put(int key, RefcountedResource<T> obj) {
-            checkNotNull(obj, "Null resources cannot be added");
+            Objects.requireNonNull(obj, "Null resources cannot be added");
             mArray.put(key, obj);
         }
 
@@ -1101,7 +1100,7 @@
         if (requestedSpi > 0 && requestedSpi < 256) {
             throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
         }
-        checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
+        Objects.requireNonNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
 
         int callingUid = Binder.getCallingUid();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
@@ -1218,7 +1217,7 @@
             throw new IllegalArgumentException(
                     "Specified port number must be a valid non-reserved UDP port");
         }
-        checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
+        Objects.requireNonNull(binder, "Null Binder passed to openUdpEncapsulationSocket");
 
         int callingUid = Binder.getCallingUid();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid);
@@ -1278,8 +1277,8 @@
             String localAddr, String remoteAddr, Network underlyingNetwork, IBinder binder,
             String callingPackage) {
         enforceTunnelFeatureAndPermissions(callingPackage);
-        checkNotNull(binder, "Null Binder passed to createTunnelInterface");
-        checkNotNull(underlyingNetwork, "No underlying network was specified");
+        Objects.requireNonNull(binder, "Null Binder passed to createTunnelInterface");
+        Objects.requireNonNull(underlyingNetwork, "No underlying network was specified");
         checkInetAddress(localAddr);
         checkInetAddress(remoteAddr);
 
@@ -1556,7 +1555,7 @@
                     "IPsec Tunnel Mode requires PackageManager.FEATURE_IPSEC_TUNNELS");
         }
 
-        checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
+        Objects.requireNonNull(callingPackage, "Null calling package cannot create IpSec tunnels");
         switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
             case AppOpsManager.MODE_DEFAULT:
                 mContext.enforceCallingOrSelfPermission(
@@ -1625,12 +1624,12 @@
     @Override
     public synchronized IpSecTransformResponse createTransform(
             IpSecConfig c, IBinder binder, String callingPackage) throws RemoteException {
-        checkNotNull(c);
+        Objects.requireNonNull(c);
         if (c.getMode() == IpSecTransform.MODE_TUNNEL) {
             enforceTunnelFeatureAndPermissions(callingPackage);
         }
         checkIpSecConfig(c);
-        checkNotNull(binder, "Null Binder passed to createTransform");
+        Objects.requireNonNull(binder, "Null Binder passed to createTransform");
         final int resourceId = mNextResourceId++;
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cad917b..c5f1923 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -23,7 +23,6 @@
 import static android.location.LocationManager.PASSIVE_PROVIDER;
 import static android.os.PowerManager.locationPowerSaveModeToString;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.Preconditions.checkState;
 
 import android.Manifest;
@@ -121,6 +120,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -821,7 +821,7 @@
 
         @GuardedBy("mLock")
         public void attachLocked(AbstractLocationProvider provider) {
-            checkNotNull(provider);
+            Objects.requireNonNull(provider);
             checkState(mProvider == null);
 
             if (D) {
@@ -1430,7 +1430,7 @@
     @Override
     public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
             String featureId, String listenerIdentifier) {
-        Preconditions.checkNotNull(listenerIdentifier);
+        Objects.requireNonNull(listenerIdentifier);
 
         return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
                 callback, packageName, featureId, listenerIdentifier);
@@ -2119,7 +2119,7 @@
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
             PendingIntent intent, String packageName, String featureId,
             String listenerIdentifier) {
-        Preconditions.checkNotNull(listenerIdentifier);
+        Objects.requireNonNull(listenerIdentifier);
 
         synchronized (mLock) {
             if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2470,7 +2470,7 @@
     @Override
     public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
             String packageName, String featureId, String listenerIdentifier) {
-        Preconditions.checkNotNull(listenerIdentifier);
+        Objects.requireNonNull(listenerIdentifier);
 
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2567,7 +2567,7 @@
     @Override
     public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
             String packageName, String featureId, String listenerIdentifier) {
-        Preconditions.checkNotNull(listenerIdentifier);
+        Objects.requireNonNull(listenerIdentifier);
 
         return mGnssManagerService == null ? false
                 : mGnssManagerService.addGnssMeasurementsListener(listener, packageName, featureId,
@@ -2600,7 +2600,7 @@
     @Override
     public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
             String packageName, String featureId, String listenerIdentifier) {
-        Preconditions.checkNotNull(listenerIdentifier);
+        Objects.requireNonNull(listenerIdentifier);
 
         return mGnssManagerService == null ? false
                 : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index 92d1da5..9b1326b 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -140,11 +140,6 @@
         }
 
         @Override
-        public Bundle getCarrierConfigValues(int subId) throws RemoteException {
-            return null;
-        }
-
-        @Override
         public Uri importTextMessage(String callingPkg, String address, int type, String text,
                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
             return null;
@@ -373,12 +368,6 @@
         }
 
         @Override
-        public Bundle getCarrierConfigValues(int subId) throws RemoteException {
-            Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
-            return getServiceGuarded().getCarrierConfigValues(subId);
-        }
-
-        @Override
         public Uri importTextMessage(String callingPkg, String address, int type, String text,
                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index ad02aad..eac767f 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -46,6 +46,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.LinkedList;
+import java.util.Objects;
 
 /**
  * Generic connector class for interfacing with a native daemon which uses the
@@ -126,7 +127,7 @@
      */
     public void setWarnIfHeld(Object warnIfHeld) {
         Preconditions.checkState(mWarnIfHeld == null);
-        mWarnIfHeld = Preconditions.checkNotNull(warnIfHeld);
+        mWarnIfHeld = Objects.requireNonNull(warnIfHeld);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 0d496b6..1f73650 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -109,6 +109,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * @hide
@@ -456,7 +457,7 @@
     @Override
     public void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name) {
         NetworkStack.checkNetworkStackPermission(mContext);
-        Preconditions.checkNotNull(provider);
+        Objects.requireNonNull(provider);
         synchronized(mTetheringStatsProviders) {
             mTetheringStatsProviders.put(provider, name);
         }
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
index 39be311..cfe56052 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateServiceImpl.java
@@ -18,6 +18,8 @@
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.timedetector.NetworkTimeSuggestion;
+import android.app.timedetector.TimeDetector;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -35,10 +37,10 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.NtpTrustedTime;
 import android.util.TimeUtils;
+import android.util.TimestampedValue;
 
 import com.android.internal.util.DumpUtils;
 
@@ -46,21 +48,19 @@
 import java.io.PrintWriter;
 
 /**
- * Monitors the network time and updates the system time if it is out of sync
- * and there hasn't been any NITZ update from the carrier recently.
- * If looking up the network time fails for some reason, it tries a few times with a short
- * interval and then resets to checking on longer intervals.
- * <p>
- * If the user enables AUTO_TIME, it will check immediately for the network time, if NITZ wasn't
- * available.
- * </p>
+ * Monitors the network time. If looking up the network time fails for some reason, it tries a few
+ * times with a short interval and then resets to checking on longer intervals.
+ *
+ * <p>When available, the time is always suggested to the {@link
+ * com.android.server.timedetector.TimeDetectorService} where it may be used to set the device
+ * system clock, depending on user settings and what other signals are available.
  */
 public class NetworkTimeUpdateServiceImpl extends Binder implements NetworkTimeUpdateService {
 
     private static final String TAG = "NetworkTimeUpdateService";
     private static final boolean DBG = false;
 
-    private static final int EVENT_AUTO_TIME_CHANGED = 1;
+    private static final int EVENT_AUTO_TIME_ENABLED = 1;
     private static final int EVENT_POLL_NETWORK_TIME = 2;
     private static final int EVENT_NETWORK_CHANGED = 3;
 
@@ -69,20 +69,19 @@
 
     private static final int POLL_REQUEST = 0;
 
-    private static final long NOT_SET = -1;
-    private long mNitzTimeSetTime = NOT_SET;
     private Network mDefaultNetwork = null;
 
     private final Context mContext;
     private final NtpTrustedTime mTime;
     private final AlarmManager mAlarmManager;
+    private final TimeDetector mTimeDetector;
     private final ConnectivityManager mCM;
     private final PendingIntent mPendingPollIntent;
     private final PowerManager.WakeLock mWakeLock;
 
     // NTP lookup is done on this thread and handler
     private Handler mHandler;
-    private SettingsObserver mSettingsObserver;
+    private AutoTimeSettingObserver mAutoTimeSettingObserver;
     private NetworkTimeUpdateCallback mNetworkTimeUpdateCallback;
 
     // Normal polling frequency
@@ -91,8 +90,6 @@
     private final long mPollingIntervalShorterMs;
     // Number of times to try again
     private final int mTryAgainTimesMax;
-    // If the time difference is greater than this threshold, then update the time.
-    private final int mTimeErrorThresholdMs;
     // Keeps track of how many quick attempts were made to fetch NTP time.
     // During bootup, the network may not have been up yet, or it's taking time for the
     // connection to happen.
@@ -102,6 +99,7 @@
         mContext = context;
         mTime = NtpTrustedTime.getInstance(context);
         mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mTimeDetector = mContext.getSystemService(TimeDetector.class);
         mCM = mContext.getSystemService(ConnectivityManager.class);
 
         Intent pollIntent = new Intent(ACTION_POLL, null);
@@ -113,8 +111,6 @@
                 com.android.internal.R.integer.config_ntpPollingIntervalShorter);
         mTryAgainTimesMax = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_ntpRetry);
-        mTimeErrorThresholdMs = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_ntpThreshold);
 
         mWakeLock = context.getSystemService(PowerManager.class).newWakeLock(
                 PowerManager.PARTIAL_WAKE_LOCK, TAG);
@@ -122,7 +118,6 @@
 
     @Override
     public void systemRunning() {
-        registerForTelephonyIntents();
         registerForAlarms();
 
         HandlerThread thread = new HandlerThread(TAG);
@@ -131,14 +126,9 @@
         mNetworkTimeUpdateCallback = new NetworkTimeUpdateCallback();
         mCM.registerDefaultNetworkCallback(mNetworkTimeUpdateCallback, mHandler);
 
-        mSettingsObserver = new SettingsObserver(mHandler, EVENT_AUTO_TIME_CHANGED);
-        mSettingsObserver.observe(mContext);
-    }
-
-    private void registerForTelephonyIntents() {
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(TelephonyManager.ACTION_NETWORK_SET_TIME);
-        mContext.registerReceiver(mNitzReceiver, intentFilter);
+        mAutoTimeSettingObserver = new AutoTimeSettingObserver(mContext, mHandler,
+                EVENT_AUTO_TIME_ENABLED);
+        mAutoTimeSettingObserver.observe();
     }
 
     private void registerForAlarms() {
@@ -152,8 +142,7 @@
     }
 
     private void onPollNetworkTime(int event) {
-        // If Automatic time is not set, don't bother. Similarly, if we don't
-        // have any default network, don't bother.
+        // If we don't have any default network, don't bother.
         if (mDefaultNetwork == null) return;
         mWakeLock.acquire();
         try {
@@ -173,10 +162,12 @@
         if (mTime.getCacheAge() < mPollingIntervalMs) {
             // Obtained fresh fix; schedule next normal update
             resetAlarm(mPollingIntervalMs);
-            if (isAutomaticTimeRequested()) {
-                updateSystemClock(event);
-            }
 
+            // Suggest the time to the time detector. It may choose use it to set the system clock.
+            TimestampedValue<Long> timeSignal = mTime.getCachedNtpTimeSignal();
+            NetworkTimeSuggestion timeSuggestion = new NetworkTimeSuggestion(timeSignal);
+            timeSuggestion.addDebugInfo("Origin: NetworkTimeUpdateServiceImpl. event=" + event);
+            mTimeDetector.suggestNetworkTime(timeSuggestion);
         } else {
             // No fresh fix; schedule retry
             mTryAgainCounter++;
@@ -190,36 +181,6 @@
         }
     }
 
-    private long getNitzAge() {
-        if (mNitzTimeSetTime == NOT_SET) {
-            return Long.MAX_VALUE;
-        } else {
-            return SystemClock.elapsedRealtime() - mNitzTimeSetTime;
-        }
-    }
-
-    /**
-     * Consider updating system clock based on current NTP fix, if requested by
-     * user, significant enough delta, and we don't have a recent NITZ.
-     */
-    private void updateSystemClock(int event) {
-        final boolean forceUpdate = (event == EVENT_AUTO_TIME_CHANGED);
-        if (!forceUpdate) {
-            if (getNitzAge() < mPollingIntervalMs) {
-                if (DBG) Log.d(TAG, "Ignoring NTP update due to recent NITZ");
-                return;
-            }
-
-            final long skew = Math.abs(mTime.currentTimeMillis() - System.currentTimeMillis());
-            if (skew < mTimeErrorThresholdMs) {
-                if (DBG) Log.d(TAG, "Ignoring NTP update due to low skew");
-                return;
-            }
-        }
-
-        SystemClock.setCurrentTimeMillis(mTime.currentTimeMillis());
-    }
-
     /**
      * Cancel old alarm and starts a new one for the specified interval.
      *
@@ -232,27 +193,6 @@
         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, next, mPendingPollIntent);
     }
 
-    /**
-     * Checks if the user prefers to automatically set the time.
-     */
-    private boolean isAutomaticTimeRequested() {
-        return Settings.Global.getInt(
-                mContext.getContentResolver(), Settings.Global.AUTO_TIME, 0) != 0;
-    }
-
-    /** Receiver for Nitz time events */
-    private BroadcastReceiver mNitzReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (DBG) Log.d(TAG, "Received " + action);
-            if (TelephonyManager.ACTION_NETWORK_SET_TIME.equals(action)) {
-                mNitzTimeSetTime = SystemClock.elapsedRealtime();
-            }
-        }
-    };
-
     /** Handler to do the network accesses on */
     private class MyHandler extends Handler {
 
@@ -263,7 +203,7 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case EVENT_AUTO_TIME_CHANGED:
+                case EVENT_AUTO_TIME_ENABLED:
                 case EVENT_POLL_NETWORK_TIME:
                 case EVENT_NETWORK_CHANGED:
                     onPollNetworkTime(msg.what);
@@ -287,27 +227,42 @@
         }
     }
 
-    /** Observer to watch for changes to the AUTO_TIME setting */
-    private static class SettingsObserver extends ContentObserver {
+    /**
+     * Observer to watch for changes to the AUTO_TIME setting. It only triggers when the setting
+     * is enabled.
+     */
+    private static class AutoTimeSettingObserver extends ContentObserver {
 
-        private int mMsg;
-        private Handler mHandler;
+        private final Context mContext;
+        private final int mMsg;
+        private final Handler mHandler;
 
-        SettingsObserver(Handler handler, int msg) {
+        AutoTimeSettingObserver(Context context, Handler handler, int msg) {
             super(handler);
+            mContext = context;
             mHandler = handler;
             mMsg = msg;
         }
 
-        void observe(Context context) {
-            ContentResolver resolver = context.getContentResolver();
+        void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(Settings.Global.getUriFor(Settings.Global.AUTO_TIME),
                     false, this);
         }
 
         @Override
         public void onChange(boolean selfChange) {
-            mHandler.obtainMessage(mMsg).sendToTarget();
+            if (isAutomaticTimeEnabled()) {
+                mHandler.obtainMessage(mMsg).sendToTarget();
+            }
+        }
+
+        /**
+         * Checks if the user prefers to automatically set the time.
+         */
+        private boolean isAutomaticTimeEnabled() {
+            ContentResolver resolver = mContext.getContentResolver();
+            return Settings.Global.getInt(resolver, Settings.Global.AUTO_TIME, 0) != 0;
         }
     }
 
@@ -319,8 +274,6 @@
         pw.print("\nPollingIntervalShorterMs: ");
         TimeUtils.formatDuration(mPollingIntervalShorterMs, pw);
         pw.println("\nTryAgainTimesMax: " + mTryAgainTimesMax);
-        pw.print("TimeErrorThresholdMs: ");
-        TimeUtils.formatDuration(mTimeErrorThresholdMs, pw);
         pw.println("\nTryAgainCounter: " + mTryAgainCounter);
         pw.println("NTP cache age: " + mTime.getCacheAge());
         pw.println("NTP cache certainty: " + mTime.getCacheCertainty());
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 22fa8ff4..3d455ee 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -61,6 +61,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.PackageManager;
@@ -74,8 +75,6 @@
 import android.os.Binder;
 import android.os.DropBoxManager;
 import android.os.Environment;
-import android.os.Environment.UserEnvironment;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -183,6 +182,8 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
@@ -379,6 +380,14 @@
     @GuardedBy("mAppFuseLock")
     private AppFuseBridge mAppFuseBridge = null;
 
+    /** Matches known application dir paths. The first group contains the generic part of the path,
+     * the second group contains the user id (or null if it's a public volume without users), the
+     * third group contains the package name, and the fourth group the remainder of the path.
+     */
+    public static final Pattern KNOWN_APP_DIR_PATHS = Pattern.compile(
+            "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?");
+
+
     private VolumeInfo findVolumeByIdOrThrow(String id) {
         synchronized (mLock) {
             final VolumeInfo vol = mVolumes.get(id);
@@ -1110,7 +1119,7 @@
         // Push down current secure keyguard status so that we ignore malicious
         // USB devices while locked.
         mSecureKeyguardShowing = isShowing
-                && mContext.getSystemService(KeyguardManager.class).isDeviceSecure();
+                && mContext.getSystemService(KeyguardManager.class).isDeviceSecure(mCurrentUserId);
         try {
             mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
         } catch (Exception e) {
@@ -2102,7 +2111,7 @@
     public void setVolumeNickname(String fsUuid, String nickname) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        Preconditions.checkNotNull(fsUuid);
+        Objects.requireNonNull(fsUuid);
         synchronized (mLock) {
             final VolumeRecord rec = mRecords.get(fsUuid);
             rec.nickname = nickname;
@@ -2115,7 +2124,7 @@
     public void setVolumeUserFlags(String fsUuid, int flags, int mask) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        Preconditions.checkNotNull(fsUuid);
+        Objects.requireNonNull(fsUuid);
         synchronized (mLock) {
             final VolumeRecord rec = mRecords.get(fsUuid);
             rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
@@ -2128,7 +2137,7 @@
     public void forgetVolume(String fsUuid) {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
-        Preconditions.checkNotNull(fsUuid);
+        Objects.requireNonNull(fsUuid);
 
         synchronized (mLock) {
             final VolumeRecord rec = mRecords.remove(fsUuid);
@@ -2534,7 +2543,7 @@
 
     @Override
     public String getMountedObbPath(String rawPath) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
 
         warnOnNotMounted();
 
@@ -2552,7 +2561,7 @@
 
     @Override
     public boolean isObbMounted(String rawPath) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
         synchronized (mObbMounts) {
             return mObbPathToStateMap.containsKey(rawPath);
         }
@@ -2561,10 +2570,10 @@
     @Override
     public void mountObb(String rawPath, String canonicalPath, String key,
             IObbActionListener token, int nonce, ObbInfo obbInfo) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
-        Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
-        Preconditions.checkNotNull(token, "token cannot be null");
-        Preconditions.checkNotNull(obbInfo, "obbIfno cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null");
+        Objects.requireNonNull(token, "token cannot be null");
+        Objects.requireNonNull(obbInfo, "obbIfno cannot be null");
 
         final int callingUid = Binder.getCallingUid();
         final ObbState obbState = new ObbState(rawPath, canonicalPath,
@@ -2578,7 +2587,7 @@
 
     @Override
     public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
-        Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+        Objects.requireNonNull(rawPath, "rawPath cannot be null");
 
         final ObbState existingState;
         synchronized (mObbMounts) {
@@ -3135,7 +3144,6 @@
     public void mkdirs(String callingPkg, String appPath) {
         final int callingUid = Binder.getCallingUid();
         final int userId = UserHandle.getUserId(callingUid);
-        final UserEnvironment userEnv = new UserEnvironment(userId);
         final String propertyName = "sys.user." + userId + ".ce_available";
 
         // Ignore requests to create directories while storage is locked
@@ -3161,25 +3169,36 @@
             throw new IllegalStateException("Failed to resolve " + appPath + ": " + e);
         }
 
-        // Try translating the app path into a vold path, but require that it
-        // belong to the calling package.
-        if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
-                FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
-                FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
-            appPath = appFile.getAbsolutePath();
-            if (!appPath.endsWith("/")) {
-                appPath = appPath + "/";
+        appPath = appFile.getAbsolutePath();
+        if (!appPath.endsWith("/")) {
+            appPath = appPath + "/";
+        }
+        // Ensure that the path we're asked to create is a known application directory
+        // path.
+        final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(appPath);
+        if (matcher.matches()) {
+            // And that the package dir matches the calling package
+            if (!matcher.group(3).equals(callingPkg)) {
+                throw new SecurityException("Invalid mkdirs path: " + appFile
+                        + " does not contain calling package " + callingPkg);
             }
-
+            // And that the user id part of the path (if any) matches the calling user id,
+            // or if for a public volume (no user id), the user matches the current user
+            if ((matcher.group(2) != null && !matcher.group(2).equals(Integer.toString(userId)))
+                    || (matcher.group(2) == null && userId != mCurrentUserId)) {
+                throw new SecurityException("Invalid mkdirs path: " + appFile
+                        + " does not match calling user id " + userId);
+            }
             try {
-                mVold.mkdirs(appPath);
-                return;
-            } catch (Exception e) {
+                mVold.setupAppDir(appPath, matcher.group(1), callingUid);
+            } catch (RemoteException e) {
                 throw new IllegalStateException("Failed to prepare " + appPath + ": " + e);
             }
-        }
 
-        throw new SecurityException("Invalid mkdirs path: " + appFile);
+            return;
+        }
+        throw new SecurityException("Invalid mkdirs path: " + appFile
+                + " is not a known app path.");
     }
 
     @Override
@@ -3898,8 +3917,22 @@
 
             // Otherwise we're willing to give out sandboxed or non-sandboxed if
             // they hold the runtime permission
-            final boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,
+            boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE,
                     uid, packageName) == MODE_ALLOWED;
+
+            // Hack(b/147137425): we have to honor hasRequestedLegacyExternalStorage for a short
+            // while to enable 2 cases.
+            // 1) Apps that want to be in scoped storage in R, but want to opt out in Q devices,
+            // because they want to use raw file paths, would fail until fuse is enabled by default.
+            // 2) Test apps that target current sdk will fail. They would fail even after fuse is
+            // enabled, but we are fixing it with b/142395442. We are not planning to enable
+            // fuse by default until b/142395442 is fixed.
+            if (!hasLegacy && !mIsFuseEnabled) {
+                ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
+                        0, UserHandle.getUserId(uid));
+                hasLegacy = ai.hasRequestedLegacyExternalStorage();
+            }
+
             if (hasLegacy && hasWrite) {
                 return Zygote.MOUNT_EXTERNAL_WRITE;
             } else if (hasLegacy && hasRead) {
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 5ed94e3..179780d 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -73,7 +74,7 @@
      */
     public static @NonNull Future<?> submit(@NonNull Runnable runnable,
             @NonNull String description) {
-        Preconditions.checkNotNull(description, "description cannot be null");
+        Objects.requireNonNull(description, "description cannot be null");
 
         SystemServerInitThreadPool instance;
         synchronized (LOCK) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c741fce..3e6ccb5 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -23,6 +23,7 @@
 
 import static java.util.Arrays.copyOf;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
@@ -41,6 +42,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.telephony.Annotation;
+import android.telephony.Annotation.ApnType;
 import android.telephony.Annotation.DataFailureCause;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SrvccState;
@@ -49,6 +51,13 @@
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
+import android.telephony.CellSignalStrength;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthNr;
+import android.telephony.CellSignalStrengthTdscdma;
+import android.telephony.CellSignalStrengthWcdma;
 import android.telephony.DataFailCause;
 import android.telephony.DisconnectCause;
 import android.telephony.LocationAccessPolicy;
@@ -74,7 +83,6 @@
 import com.android.internal.telephony.IOnSubscriptionsChangedListener;
 import com.android.internal.telephony.IPhoneStateListener;
 import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyPermissions;
 import com.android.internal.util.ArrayUtils;
@@ -261,8 +269,8 @@
     private final LocalLog mListenLog = new LocalLog(100);
 
     // Per-phoneMap of APN Type to DataConnectionState
-    private List<Map<String, PreciseDataConnectionState>> mPreciseDataConnectionStates =
-            new ArrayList<Map<String, PreciseDataConnectionState>>();
+    private List<Map<Integer, PreciseDataConnectionState>> mPreciseDataConnectionStates =
+            new ArrayList<Map<Integer, PreciseDataConnectionState>>();
 
     // Nothing here yet, but putting it here in case we want to add more in the future.
     static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
@@ -274,11 +282,12 @@
     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
+                        | PhoneStateListener.LISTEN_REGISTRATION_FAILURE;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
-                PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
+                PhoneStateListener.LISTEN_PRECISE_CALL_STATE
+                | PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
 
     static final int READ_ACTIVE_EMERGENCY_SESSION_PERMISSION_MASK =
             PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL
@@ -402,7 +411,11 @@
         mVoiceActivationState = copyOf(mVoiceActivationState, mNumPhones);
         mDataActivationState = copyOf(mDataActivationState, mNumPhones);
         mUserMobileDataState = copyOf(mUserMobileDataState, mNumPhones);
-        mSignalStrength = copyOf(mSignalStrength, mNumPhones);
+        if (mSignalStrength != null) {
+            mSignalStrength = copyOf(mSignalStrength, mNumPhones);
+        } else {
+            mSignalStrength = new SignalStrength[mNumPhones];
+        }
         mMessageWaiting = copyOf(mMessageWaiting, mNumPhones);
         mCallForwarding = copyOf(mCallForwarding, mNumPhones);
         mCellIdentity = copyOf(mCellIdentity, mNumPhones);
@@ -436,7 +449,7 @@
             mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
             mCallIncomingNumber[i] =  "";
             mServiceState[i] =  new ServiceState();
-            mSignalStrength[i] =  new SignalStrength();
+            mSignalStrength[i] =  null;
             mUserMobileDataState[i] = false;
             mMessageWaiting[i] =  false;
             mCallForwarding[i] =  false;
@@ -454,7 +467,7 @@
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
-            mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
+            mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
         }
     }
 
@@ -520,7 +533,7 @@
             mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
             mCallIncomingNumber[i] =  "";
             mServiceState[i] =  new ServiceState();
-            mSignalStrength[i] =  new SignalStrength();
+            mSignalStrength[i] =  null;
             mUserMobileDataState[i] = false;
             mMessageWaiting[i] =  false;
             mCallForwarding[i] =  false;
@@ -538,7 +551,7 @@
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mForegroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
             mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
-            mPreciseDataConnectionStates.add(new HashMap<String, PreciseDataConnectionState>());
+            mPreciseDataConnectionStates.add(new HashMap<Integer, PreciseDataConnectionState>());
         }
 
         mAppOps = mContext.getSystemService(AppOpsManager.class);
@@ -785,9 +798,11 @@
                             if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 r.callback.onServiceStateChanged(rawSs);
                             } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false));
+                                r.callback.onServiceStateChanged(
+                                        rawSs.createLocationInfoSanitizedCopy(false));
                             } else {
-                                r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true));
+                                r.callback.onServiceStateChanged(
+                                        rawSs.createLocationInfoSanitizedCopy(true));
                             }
                         } catch (RemoteException ex) {
                             remove(r.binder);
@@ -795,10 +810,12 @@
                     }
                     if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
                         try {
-                            int gsmSignalStrength = mSignalStrength[phoneId]
-                                    .getGsmSignalStrength();
-                            r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
-                                    : gsmSignalStrength));
+                            if (mSignalStrength[phoneId] != null) {
+                                int gsmSignalStrength = mSignalStrength[phoneId]
+                                        .getGsmSignalStrength();
+                                r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+                                        : gsmSignalStrength));
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -855,7 +872,9 @@
                     }
                     if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
                         try {
-                            r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                            if (mSignalStrength[phoneId] != null) {
+                                r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                            }
                         } catch (RemoteException ex) {
                             remove(r.binder);
                         }
@@ -1142,9 +1161,9 @@
                             if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                                 stateToSend = new ServiceState(state);
                             } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
-                                stateToSend = state.sanitizeLocationInfo(false);
+                                stateToSend = state.createLocationInfoSanitizedCopy(false);
                             } else {
-                                stateToSend = state.sanitizeLocationInfo(true);
+                                stateToSend = state.createLocationInfoSanitizedCopy(true);
                             }
                             if (DBG) {
                                 log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
@@ -1288,7 +1307,7 @@
     public void notifyCarrierNetworkChange(boolean active) {
         // only CarrierService with carrier privilege rule should have the permission
         int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
-                    .getActiveSubscriptionIdList(false))
+                    .getActiveAndHiddenSubscriptionIdList())
                     .filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext,
                             i)).toArray();
         if (ArrayUtils.isEmpty(subIds)) {
@@ -1477,11 +1496,12 @@
      *
      * @param phoneId the phoneId carrying the data connection
      * @param subId the subscriptionId for the data connection
-     * @param apnType the APN type that triggered a change in the data connection
+     * @param apnType the apn type bitmask, defined with {@code ApnSetting#TYPE_*} flags.
      * @param preciseState a PreciseDataConnectionState that has info about the data connection
      */
+    @Override
     public void notifyDataConnectionForSubscriber(
-            int phoneId, int subId, String apnType, PreciseDataConnectionState preciseState) {
+            int phoneId, int subId, @ApnType int apnType, PreciseDataConnectionState preciseState) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
@@ -1507,7 +1527,7 @@
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 // We only call the callback when the change is for default APN type.
-                if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)
+                if ((ApnSetting.TYPE_DEFAULT & apnType) != 0
                         && (mDataConnectionState[phoneId] != state
                         || mDataConnectionNetworkType[phoneId] != networkType)) {
                     String str = "onDataConnectionStateChanged("
@@ -1576,7 +1596,7 @@
         loge("This function should not be invoked");
     }
 
-    private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, String apnType) {
+    private void notifyDataConnectionFailedForSubscriber(int phoneId, int subId, int apnType) {
         if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
             return;
         }
@@ -1591,7 +1611,7 @@
                         new PreciseDataConnectionState(
                                 TelephonyManager.DATA_UNKNOWN,
                                 TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                                ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
+                                apnType, null, null,
                                 DataFailCause.NONE, null));
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
@@ -1760,7 +1780,8 @@
         }
     }
 
-    public void notifyPreciseDataConnectionFailed(int phoneId, int subId, String apnType,
+    @Override
+    public void notifyPreciseDataConnectionFailed(int phoneId, int subId, @ApnType int apnType,
             String apn, @DataFailureCause int failCause) {
         if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
             return;
@@ -1776,7 +1797,7 @@
                         new PreciseDataConnectionState(
                                 TelephonyManager.DATA_UNKNOWN,
                                 TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                                ApnSetting.getApnTypesBitmaskFromString(apnType), null, null,
+                                apnType, null, null,
                                 failCause, null));
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
@@ -2049,6 +2070,40 @@
     }
 
     @Override
+    public void notifyRegistrationFailed(int phoneId, int subId, @NonNull CellIdentity cellIdentity,
+            @NonNull String chosenPlmn, int domain, int causeCode, int additionalCauseCode) {
+        if (!checkNotifyPermission("notifyRegistrationFailed()")) {
+            return;
+        }
+
+        // In case callers don't have fine location access, pre-construct a location-free version
+        // of the CellIdentity. This will still have the PLMN ID, which should be sufficient for
+        // most purposes.
+        final CellIdentity noLocationCi = cellIdentity.sanitizeLocationInfo();
+
+        synchronized (mRecords) {
+            if (validatePhoneId(phoneId)) {
+                for (Record r : mRecords) {
+                    if (r.matchPhoneStateListenerEvent(
+                            PhoneStateListener.LISTEN_REGISTRATION_FAILURE)
+                            && idMatch(r.subId, subId, phoneId)) {
+                        try {
+                            r.callback.onRegistrationFailed(
+                                    checkFineLocationAccess(r, Build.VERSION_CODES.R)
+                                            ? cellIdentity : noLocationCi,
+                                    chosenPlmn, domain, causeCode,
+                                    additionalCauseCode);
+                        } catch (RemoteException ex) {
+                            mRemoveList.add(r.binder);
+                        }
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
+    }
+
+    @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
 
@@ -2124,10 +2179,19 @@
     /** Fired when a subscription's phone state changes. */
     private static final String ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED =
             "android.intent.action.SUBSCRIPTION_PHONE_STATE";
+    /**
+     * Broadcast Action: The data connection state has changed for any one of the
+     * phone's mobile data connections (eg, default, MMS or GPS specific connection).
+     */
+    private static final String ACTION_ANY_DATA_CONNECTION_STATE_CHANGED =
+            "android.intent.action.ANY_DATA_STATE";
 
     // Legacy intent extra keys, copied from PhoneConstants.
     // Used in legacy intents sent here, for backward compatibility.
+    private static final String PHONE_CONSTANTS_DATA_APN_TYPE_KEY = "apnType";
+    private static final String PHONE_CONSTANTS_DATA_APN_KEY = "apn";
     private static final String PHONE_CONSTANTS_SLOT_KEY = "slot";
+    private static final String PHONE_CONSTANTS_STATE_KEY = "state";
     private static final String PHONE_CONSTANTS_SUBSCRIPTION_KEY = "subscription";
 
     private void broadcastServiceStateChanged(ServiceState state, int phoneId, int subId) {
@@ -2140,7 +2204,7 @@
             Binder.restoreCallingIdentity(ident);
         }
 
-        Intent intent = new Intent(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);
+        Intent intent = new Intent(Intent.ACTION_SERVICE_STATE);
         intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         Bundle data = new Bundle();
         state.fillInNotifierBundle(data);
@@ -2166,13 +2230,32 @@
 
         Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
         Bundle data = new Bundle();
-        signalStrength.fillInNotifierBundle(data);
+        fillInSignalStrengthNotifierBundle(signalStrength, data);
         intent.putExtras(data);
         intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
         intent.putExtra(PHONE_CONSTANTS_SLOT_KEY, phoneId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
+    private void fillInSignalStrengthNotifierBundle(SignalStrength signalStrength, Bundle bundle) {
+        List<CellSignalStrength> cellSignalStrengths = signalStrength.getCellSignalStrengths();
+        for (CellSignalStrength cellSignalStrength : cellSignalStrengths) {
+            if (cellSignalStrength instanceof CellSignalStrengthLte) {
+                bundle.putParcelable("Lte", (CellSignalStrengthLte) cellSignalStrength);
+            } else if (cellSignalStrength instanceof CellSignalStrengthCdma) {
+                bundle.putParcelable("Cdma", (CellSignalStrengthCdma) cellSignalStrength);
+            } else if (cellSignalStrength instanceof CellSignalStrengthGsm) {
+                bundle.putParcelable("Gsm", (CellSignalStrengthGsm) cellSignalStrength);
+            } else if (cellSignalStrength instanceof CellSignalStrengthWcdma) {
+                bundle.putParcelable("Wcdma", (CellSignalStrengthWcdma) cellSignalStrength);
+            } else if (cellSignalStrength instanceof CellSignalStrengthTdscdma) {
+                bundle.putParcelable("Tdscdma", (CellSignalStrengthTdscdma) cellSignalStrength);
+            } else if (cellSignalStrength instanceof CellSignalStrengthNr) {
+                bundle.putParcelable("Nr", (CellSignalStrengthNr) cellSignalStrength);
+            }
+        }
+    }
+
     /**
      * Broadcasts an intent notifying apps of a phone state change. {@code subId} can be
      * a valid subId, in which case this function fires a subId-specific intent, or it
@@ -2246,19 +2329,50 @@
     }
 
     private void broadcastDataConnectionStateChanged(int state, String apn,
-                                                     String apnType, int subId) {
+                                                     int apnType, int subId) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
-        Intent intent = new Intent(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        intent.putExtra(TelephonyManager.EXTRA_STATE, dataStateToString(state));
-
-        intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
-        intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
+        Intent intent = new Intent(ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
+        intent.putExtra(PHONE_CONSTANTS_STATE_KEY, dataStateToString(state));
+        intent.putExtra(PHONE_CONSTANTS_DATA_APN_KEY, apn);
+        intent.putExtra(PHONE_CONSTANTS_DATA_APN_TYPE_KEY, getApnTypesStringFromBitmask(apnType));
         intent.putExtra(PHONE_CONSTANTS_SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    static {
+        APN_TYPE_INT_MAP = new android.util.ArrayMap<Integer, String>();
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_DEFAULT, "default");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_MMS, "mms");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_SUPL, "supl");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_DUN, "dun");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_HIPRI, "hipri");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_FOTA, "fota");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_IMS, "ims");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_CBS, "cbs");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_IA, "ia");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_EMERGENCY, "emergency");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_MCX, "mcx");
+        APN_TYPE_INT_MAP.put(ApnSetting.TYPE_XCAP, "xcap");
+    }
+
+    /**
+     * Copy of ApnSetting#getApnTypesStringFromBitmask for legacy broadcast.
+     * @param apnTypeBitmask bitmask of APN types.
+     * @return comma delimited list of APN types.
+     */
+    private static String getApnTypesStringFromBitmask(int apnTypeBitmask) {
+        List<String> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((apnTypeBitmask & type) == type) {
+                types.add(APN_TYPE_INT_MAP.get(type));
+            }
+        }
+        return android.text.TextUtils.join(",", types);
+    }
+
     private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
         if (checkNotifyPermission()) {
             return;
@@ -2481,11 +2595,14 @@
 
         if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
             try {
-                SignalStrength signalStrength = mSignalStrength[phoneId];
-                if (DBG) {
-                    log("checkPossibleMissNotify: onSignalStrengthsChanged SS=" + signalStrength);
+                if (mSignalStrength[phoneId] != null) {
+                    SignalStrength signalStrength = mSignalStrength[phoneId];
+                    if (DBG) {
+                        log("checkPossibleMissNotify: onSignalStrengthsChanged SS="
+                                + signalStrength);
+                    }
+                    r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
                 }
-                r.callback.onSignalStrengthsChanged(new SignalStrength(signalStrength));
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
             }
@@ -2493,14 +2610,16 @@
 
         if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTH) != 0) {
             try {
-                int gsmSignalStrength = mSignalStrength[phoneId]
-                        .getGsmSignalStrength();
-                if (DBG) {
-                    log("checkPossibleMissNotify: onSignalStrengthChanged SS=" +
-                            gsmSignalStrength);
+                if (mSignalStrength[phoneId] != null) {
+                    int gsmSignalStrength = mSignalStrength[phoneId]
+                            .getGsmSignalStrength();
+                    if (DBG) {
+                        log("checkPossibleMissNotify: onSignalStrengthChanged SS="
+                                + gsmSignalStrength);
+                    }
+                    r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
+                            : gsmSignalStrength));
                 }
-                r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
-                        : gsmSignalStrength));
             } catch (RemoteException ex) {
                 mRemoveList.add(r.binder);
             }
@@ -2649,8 +2768,14 @@
                 return "TD_SCDMA";
             case TelephonyManager.NETWORK_TYPE_IWLAN:
                 return "IWLAN";
-            case TelephonyManager.NETWORK_TYPE_LTE_CA:
-                return "LTE_CA";
+
+            //TODO: This network type is marked as hidden because it is not a
+            // true network type and we are looking to remove it completely from the available list
+            // of network types.  Since this method is only used for logging, in the event that this
+            // network type is selected, the log will read as "Unknown."
+            //case TelephonyManager.NETWORK_TYPE_LTE_CA:
+            //    return "LTE_CA";
+
             case TelephonyManager.NETWORK_TYPE_NR:
                 return "NR";
             default:
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index d19d2dd..c27b0da 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -16,8 +16,6 @@
 
 package com.android.server;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -55,6 +53,7 @@
 import java.net.NetworkInterface;
 import java.net.SocketException;
 import java.util.ArrayList;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /** @hide */
@@ -82,9 +81,9 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
 
-        mContext = checkNotNull(context, "missing Context");
-        mNMS = checkNotNull(netManager, "missing INetworkManagementService");
-        mNetd = checkNotNull(NetdService.getInstance(), "could not get netd instance");
+        mContext = Objects.requireNonNull(context, "missing Context");
+        mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
+        mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
     }
 
     /**
@@ -96,7 +95,7 @@
     private TestNetworkInterface createInterface(boolean isTun, LinkAddress[] linkAddrs) {
         enforceTestNetworkPermissions(mContext);
 
-        checkNotNull(linkAddrs, "missing linkAddrs");
+        Objects.requireNonNull(linkAddrs, "missing linkAddrs");
 
         String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
         String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
@@ -233,8 +232,8 @@
             int callingUid,
             @NonNull IBinder binder)
             throws RemoteException, SocketException {
-        checkNotNull(looper, "missing Looper");
-        checkNotNull(context, "missing Context");
+        Objects.requireNonNull(looper, "missing Looper");
+        Objects.requireNonNull(context, "missing Context");
         // iface and binder validity checked by caller
 
         // Build network info with special testing type
@@ -267,7 +266,7 @@
         // Find the currently assigned addresses, and add them to LinkProperties
         boolean allowIPv4 = false, allowIPv6 = false;
         NetworkInterface netIntf = NetworkInterface.getByName(iface);
-        checkNotNull(netIntf, "No such network interface found: " + netIntf);
+        Objects.requireNonNull(netIntf, "No such network interface found: " + netIntf);
 
         for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
             lp.addLinkAddress(
@@ -305,8 +304,8 @@
             @NonNull IBinder binder) {
         enforceTestNetworkPermissions(mContext);
 
-        checkNotNull(iface, "missing Iface");
-        checkNotNull(binder, "missing IBinder");
+        Objects.requireNonNull(iface, "missing Iface");
+        Objects.requireNonNull(binder, "missing IBinder");
 
         if (!(iface.startsWith(INetd.IPSEC_INTERFACE_PREFIX)
                 || iface.startsWith(TEST_TUN_PREFIX))) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 76a8f92..27e0d52 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -32,7 +32,6 @@
 import android.hardware.vibrator.IVibrator;
 import android.hardware.vibrator.V1_0.EffectStrength;
 import android.icu.text.DateFormat;
-import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -54,6 +53,7 @@
 import android.os.SystemClock;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.WorkSource;
@@ -107,8 +107,9 @@
     private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255
     private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255
 
-    // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
-    private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
+    // Default vibration attributes. Used when vibration is requested without attributes
+    private static final VibrationAttributes DEFAULT_ATTRIBUTES =
+            new VibrationAttributes.Builder().build();
 
     // If HAL supports callbacks set the timeout to ASYNC_TIMEOUT_MULTIPLIER * duration.
     private static final long ASYNC_TIMEOUT_MULTIPLIER = 2;
@@ -163,7 +164,7 @@
     private int mHapticFeedbackIntensity;
     private int mNotificationIntensity;
     private int mRingIntensity;
-    private SparseArray<Pair<VibrationEffect, AudioAttributes>> mAlwaysOnEffects =
+    private SparseArray<Pair<VibrationEffect, VibrationAttributes>> mAlwaysOnEffects =
             new SparseArray<>();
 
     static native boolean vibratorExists();
@@ -207,7 +208,7 @@
         // with other system events, any duration calculations should be done use startTime so as
         // not to be affected by discontinuities created by RTC adjustments.
         public final long startTimeDebug;
-        public final AudioAttributes attrs;
+        public final VibrationAttributes attrs;
         public final int uid;
         public final String opPkg;
         public final String reason;
@@ -220,7 +221,7 @@
         public VibrationEffect originalEffect;
 
         private Vibration(IBinder token, VibrationEffect effect,
-                AudioAttributes attrs, int uid, String opPkg, String reason) {
+                VibrationAttributes attrs, int uid, String opPkg, String reason) {
             this.token = token;
             this.effect = effect;
             this.startTime = SystemClock.elapsedRealtime();
@@ -253,28 +254,7 @@
         }
 
         public boolean isHapticFeedback() {
-            if (VibratorService.this.isHapticFeedback(attrs.getUsage())) {
-                return true;
-            }
-            if (effect instanceof VibrationEffect.Prebaked) {
-                VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
-                switch (prebaked.getId()) {
-                    case VibrationEffect.EFFECT_CLICK:
-                    case VibrationEffect.EFFECT_DOUBLE_CLICK:
-                    case VibrationEffect.EFFECT_HEAVY_CLICK:
-                    case VibrationEffect.EFFECT_TEXTURE_TICK:
-                    case VibrationEffect.EFFECT_TICK:
-                    case VibrationEffect.EFFECT_POP:
-                    case VibrationEffect.EFFECT_THUD:
-                        return true;
-                    default:
-                        Slog.w(TAG, "Unknown prebaked vibration effect, "
-                                + "assuming it isn't haptic feedback.");
-                        return false;
-                }
-            }
-            final long duration = effect.getDuration();
-            return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
+            return VibratorService.this.isHapticFeedback(attrs.getUsage());
         }
 
         public boolean isNotification() {
@@ -303,13 +283,13 @@
         private final long mStartTimeDebug;
         private final VibrationEffect mEffect;
         private final VibrationEffect mOriginalEffect;
-        private final AudioAttributes mAttrs;
+        private final VibrationAttributes mAttrs;
         private final int mUid;
         private final String mOpPkg;
         private final String mReason;
 
-        public VibrationInfo(long startTimeDebug, VibrationEffect effect,
-                VibrationEffect originalEffect, AudioAttributes attrs, int uid,
+        VibrationInfo(long startTimeDebug, VibrationEffect effect,
+                VibrationEffect originalEffect, VibrationAttributes attrs, int uid,
                 String opPkg, String reason) {
             mStartTimeDebug = startTimeDebug;
             mEffect = effect;
@@ -528,7 +508,7 @@
     }
 
     @Override // Binder call
-    public boolean setAlwaysOnEffect(int id, VibrationEffect effect, AudioAttributes attrs) {
+    public boolean setAlwaysOnEffect(int id, VibrationEffect effect, VibrationAttributes attrs) {
         if (!hasPermission(android.Manifest.permission.VIBRATE_ALWAYS_ON)) {
             throw new SecurityException("Requires VIBRATE_ALWAYS_ON permission");
         }
@@ -550,8 +530,7 @@
                 return false;
             }
             if (attrs == null) {
-                attrs = new AudioAttributes.Builder()
-                        .setUsage(AudioAttributes.USAGE_UNKNOWN)
+                attrs = new VibrationAttributes.Builder()
                         .build();
             }
             synchronized (mLock) {
@@ -610,7 +589,7 @@
 
     @Override // Binder call
     public void vibrate(int uid, String opPkg, VibrationEffect effect,
-            @Nullable AudioAttributes attrs, String reason, IBinder token) {
+            @Nullable VibrationAttributes attrs, String reason, IBinder token) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
         try {
             if (!hasPermission(android.Manifest.permission.VIBRATE)) {
@@ -626,18 +605,16 @@
             }
 
             if (attrs == null) {
-                attrs = new AudioAttributes.Builder()
-                        .setUsage(AudioAttributes.USAGE_UNKNOWN)
-                        .build();
+                attrs = DEFAULT_ATTRIBUTES;
             }
 
             if (shouldBypassDnd(attrs)) {
                 if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
                         || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
                         || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
-                    final int flags = attrs.getAllFlags()
-                            & ~AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
-                    attrs = new AudioAttributes.Builder(attrs).replaceFlags(flags).build();
+                    final int flags = attrs.getFlags()
+                            & ~VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY;
+                    attrs = new VibrationAttributes.Builder(attrs).replaceFlags(flags).build();
                 }
             }
 
@@ -868,18 +845,10 @@
             return true;
         }
 
-        if (vib.attrs.getUsage() == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
-            return true;
-        }
-
-        if (vib.attrs.getUsage() == AudioAttributes.USAGE_ALARM
-                || vib.attrs.getUsage() == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
-                || vib.attrs.getUsage()
-                    == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
-            return true;
-        }
-
-        return false;
+        int usage = vib.attrs.getUsage();
+        return usage == VibrationAttributes.USAGE_RINGTONE
+                || usage == VibrationAttributes.USAGE_ALARM
+                || usage == VibrationAttributes.USAGE_COMMUNICATION_REQUEST;
     }
 
     private int getCurrentIntensityLocked(Vibration vib) {
@@ -968,13 +937,13 @@
         }
     }
 
-    private static boolean shouldBypassDnd(AudioAttributes attrs) {
-        return (attrs.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
+    private static boolean shouldBypassDnd(VibrationAttributes attrs) {
+        return attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY);
     }
 
     private int getAppOpMode(Vibration vib) {
         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
-                vib.attrs.getUsage(), vib.uid, vib.opPkg);
+                vib.attrs.getAudioAttributes().getUsage(), vib.uid, vib.opPkg);
         if (mode == AppOpsManager.MODE_ALLOWED) {
             mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
         }
@@ -1100,7 +1069,7 @@
                 mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
     }
 
-    private void updateAlwaysOnLocked(int id, VibrationEffect effect, AudioAttributes attrs) {
+    private void updateAlwaysOnLocked(int id, VibrationEffect effect, VibrationAttributes attrs) {
         // TODO: Check DND and LowPower settings
         final Vibration vib = new Vibration(null, effect, attrs, 0, null, null);
         final int intensity = getCurrentIntensityLocked(vib);
@@ -1116,7 +1085,7 @@
     private void updateAlwaysOnLocked() {
         for (int i = 0; i < mAlwaysOnEffects.size(); i++) {
             int id = mAlwaysOnEffects.keyAt(i);
-            Pair<VibrationEffect, AudioAttributes> pair = mAlwaysOnEffects.valueAt(i);
+            Pair<VibrationEffect, VibrationAttributes> pair = mAlwaysOnEffects.valueAt(i);
             updateAlwaysOnLocked(id, pair.first, pair.second);
         }
     }
@@ -1148,7 +1117,7 @@
         return vibratorExists();
     }
 
-    private void doVibratorOn(long millis, int amplitude, int uid, AudioAttributes attrs) {
+    private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
         try {
             synchronized (mInputDeviceVibrators) {
@@ -1163,7 +1132,7 @@
                 final int vibratorCount = mInputDeviceVibrators.size();
                 if (vibratorCount != 0) {
                     for (int i = 0; i < vibratorCount; i++) {
-                        mInputDeviceVibrators.get(i).vibrate(millis, attrs);
+                        mInputDeviceVibrators.get(i).vibrate(millis, attrs.getAudioAttributes());
                     }
                 } else {
                     // Note: ordering is important here! Many haptic drivers will reset their
@@ -1272,28 +1241,19 @@
     }
 
     private static boolean isNotification(int usageHint) {
-        switch (usageHint) {
-            case AudioAttributes.USAGE_NOTIFICATION:
-            case AudioAttributes.USAGE_NOTIFICATION_EVENT:
-            case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
-            case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
-            case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
-                return true;
-            default:
-                return false;
-        }
+        return usageHint == VibrationAttributes.USAGE_NOTIFICATION;
     }
 
     private static boolean isRingtone(int usageHint) {
-        return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
+        return usageHint == VibrationAttributes.USAGE_RINGTONE;
     }
 
     private static boolean isHapticFeedback(int usageHint) {
-        return usageHint == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
+        return usageHint == VibrationAttributes.USAGE_TOUCH;
     }
 
     private static boolean isAlarm(int usageHint) {
-        return usageHint == AudioAttributes.USAGE_ALARM;
+        return usageHint == VibrationAttributes.USAGE_ALARM;
     }
 
     private void noteVibratorOnLocked(int uid, long millis) {
@@ -1332,11 +1292,11 @@
     private class VibrateThread extends Thread {
         private final VibrationEffect.Waveform mWaveform;
         private final int mUid;
-        private final AudioAttributes mAttrs;
+        private final VibrationAttributes mAttrs;
 
         private boolean mForceStop;
 
-        VibrateThread(VibrationEffect.Waveform waveform, int uid, AudioAttributes attrs) {
+        VibrateThread(VibrationEffect.Waveform waveform, int uid, VibrationAttributes attrs) {
             mWaveform = waveform;
             mUid = uid;
             mAttrs = attrs;
@@ -1600,7 +1560,7 @@
                         Slog.e(TAG, "Playing external vibration: " + vib);
                     }
                 }
-                final int usage = vib.getAudioAttributes().getUsage();
+                final int usage = vib.getVibrationAttributes().getUsage();
                 final int defaultIntensity;
                 final int currentIntensity;
                 if (isRingtone(usage)) {
@@ -1731,7 +1691,7 @@
 
                 VibrationEffect effect =
                         VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
-                AudioAttributes attrs = createAudioAttributes(commonOptions);
+                VibrationAttributes attrs = createVibrationAttributes(commonOptions);
                 vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
                         mToken);
                 return 0;
@@ -1792,7 +1752,7 @@
                             amplitudesList.stream().mapToInt(Integer::intValue).toArray();
                     effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
                 }
-                AudioAttributes attrs = createAudioAttributes(commonOptions);
+                VibrationAttributes attrs = createVibrationAttributes(commonOptions);
                 vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
                         mToken);
                 return 0;
@@ -1824,7 +1784,7 @@
 
                 VibrationEffect effect =
                         VibrationEffect.get(id, false);
-                AudioAttributes attrs = createAudioAttributes(commonOptions);
+                VibrationAttributes attrs = createVibrationAttributes(commonOptions);
                 vibrate(Binder.getCallingUid(), description, effect, attrs, "Shell Command",
                         mToken);
                 return 0;
@@ -1833,13 +1793,13 @@
             }
         }
 
-        private AudioAttributes createAudioAttributes(CommonOptions commonOptions) {
+        private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) {
             final int flags = commonOptions.force
-                    ? AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
+                    ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
                     : 0;
-            return new AudioAttributes.Builder()
-                    .setUsage(AudioAttributes.USAGE_UNKNOWN)
-                    .setFlags(flags)
+            return new VibrationAttributes.Builder()
+                    .setUsage(VibrationAttributes.USAGE_UNKNOWN)
+                    .replaceFlags(flags)
                     .build();
         }
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 5996b7d..a372fca0 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -463,7 +463,7 @@
             Log.v(TAG, "addAccountExplicitly: " + account + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format("uid %s cannot explicitly add accounts of type: %s",
                     callingUid, account.type);
@@ -544,7 +544,7 @@
 
     @Override
     public Map<String, Integer> getPackagesAndVisibilityForAccount(Account account) {
-        Preconditions.checkNotNull(account, "account cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
         int callingUid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)
@@ -590,8 +590,8 @@
 
     @Override
     public int getAccountVisibility(Account account, String packageName) {
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(packageName, "packageName cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(packageName, "packageName cannot be null");
         int callingUid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)
@@ -659,7 +659,7 @@
      */
     private Integer resolveAccountVisibility(Account account, @NonNull String packageName,
             UserAccounts accounts) {
-        Preconditions.checkNotNull(packageName, "packageName cannot be null");
+        Objects.requireNonNull(packageName, "packageName cannot be null");
         int uid = -1;
         try {
             long identityToken = clearCallingIdentity();
@@ -755,8 +755,8 @@
 
     @Override
     public boolean setAccountVisibility(Account account, String packageName, int newVisibility) {
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(packageName, "packageName cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(packageName, "packageName cannot be null");
         int callingUid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)
@@ -1525,7 +1525,7 @@
                     + ", caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
         int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
@@ -1563,8 +1563,8 @@
                     account, key, callingUid, Binder.getCallingPid());
             Log.v(TAG, msg);
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(key, "key cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(key, "key cannot be null");
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format(
@@ -1715,7 +1715,7 @@
                     callingUid);
             Log.v(TAG, msg);
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format(
@@ -2401,8 +2401,8 @@
     @Override
     public void invalidateAuthToken(String accountType, String authToken) {
         int callerUid = Binder.getCallingUid();
-        Preconditions.checkNotNull(accountType, "accountType cannot be null");
-        Preconditions.checkNotNull(authToken, "authToken cannot be null");
+        Objects.requireNonNull(accountType, "accountType cannot be null");
+        Objects.requireNonNull(authToken, "authToken cannot be null");
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "invalidateAuthToken: accountType " + accountType
                     + ", caller's uid " + callerUid
@@ -2518,8 +2518,8 @@
                     + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(authTokenType, "authTokenType cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(authTokenType, "authTokenType cannot be null");
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format(
@@ -2551,8 +2551,8 @@
                     + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(authTokenType, "authTokenType cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(authTokenType, "authTokenType cannot be null");
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format(
@@ -2578,7 +2578,7 @@
                     + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format(
@@ -2644,7 +2644,7 @@
                     + ", caller's uid " + callingUid
                     + ", pid " + Binder.getCallingPid());
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
         int userId = UserHandle.getCallingUserId();
         if (!isAccountManagedByCaller(account.type, callingUid, userId)) {
             String msg = String.format(
@@ -3946,9 +3946,9 @@
         if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
             throw new SecurityException("Can be called only by system UID");
         }
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(packageName, "packageName cannot be null");
-        Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(packageName, "packageName cannot be null");
+        Objects.requireNonNull(userHandle, "userHandle cannot be null");
 
         final int userId = userHandle.getIdentifier();
 
@@ -4022,9 +4022,9 @@
             throw new SecurityException("Can be called only by system UID");
         }
 
-        Preconditions.checkNotNull(account, "account cannot be null");
-        Preconditions.checkNotNull(packageName, "packageName cannot be null");
-        Preconditions.checkNotNull(userHandle, "userHandle cannot be null");
+        Objects.requireNonNull(account, "account cannot be null");
+        Objects.requireNonNull(packageName, "packageName cannot be null");
+        Objects.requireNonNull(userHandle, "userHandle cannot be null");
 
         final int userId = userHandle.getIdentifier();
 
diff --git a/services/core/java/com/android/server/accounts/CryptoHelper.java b/services/core/java/com/android/server/accounts/CryptoHelper.java
index 2ade673..863d6b9 100644
--- a/services/core/java/com/android/server/accounts/CryptoHelper.java
+++ b/services/core/java/com/android/server/accounts/CryptoHelper.java
@@ -10,6 +10,7 @@
 
 import java.security.GeneralSecurityException;
 import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
@@ -54,7 +55,7 @@
 
     @NonNull
     /* default */ Bundle encryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
-        Preconditions.checkNotNull(bundle, "Cannot encrypt null bundle.");
+        Objects.requireNonNull(bundle, "Cannot encrypt null bundle.");
         Parcel parcel = Parcel.obtain();
         bundle.writeToParcel(parcel, 0);
         byte[] clearBytes = parcel.marshall();
@@ -76,7 +77,7 @@
 
     @Nullable
     /* default */ Bundle decryptBundle(@NonNull Bundle bundle) throws GeneralSecurityException {
-        Preconditions.checkNotNull(bundle, "Cannot decrypt null bundle.");
+        Objects.requireNonNull(bundle, "Cannot decrypt null bundle.");
         byte[] iv = bundle.getByteArray(KEY_IV);
         byte[] encryptedBytes = bundle.getByteArray(KEY_CIPHER);
         byte[] mac = bundle.getByteArray(KEY_MAC);
diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java
index 2af2f38..e38cf5f 100644
--- a/services/core/java/com/android/server/accounts/TokenCache.java
+++ b/services/core/java/com/android/server/accounts/TokenCache.java
@@ -193,7 +193,7 @@
             String packageName,
             byte[] sigDigest,
             long expiryMillis) {
-        Preconditions.checkNotNull(account);
+        Objects.requireNonNull(account);
         if (token == null || System.currentTimeMillis() > expiryMillis) {
             return;
         }
diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java
index b2c82f0..db63638 100644
--- a/services/core/java/com/android/server/am/ActiveInstrumentation.java
+++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java
@@ -67,6 +67,9 @@
     // Set to true when we have told the watcher the instrumentation is finished.
     boolean mFinished;
 
+    // The uid of the process who started this instrumentation.
+    int mSourceUid;
+
     ActiveInstrumentation(ActivityManagerService service) {
         mService = service;
     }
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 154d16f..a58bd9b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -570,7 +570,7 @@
             }
             mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
-                    true);
+                    true, false, null);
         }
 
         final ServiceMap smap = getServiceMapLocked(r.userId);
@@ -1395,7 +1395,7 @@
                         mAm.mAppOpsService.startOperation(
                                 AppOpsManager.getToken(mAm.mAppOpsService),
                                 AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName,
-                                null, true);
+                                null, true, false, "");
                         StatsLog.write(StatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
                                 r.appInfo.uid, r.shortInstanceName,
                                 StatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d7a46fe..c21adb0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -275,6 +275,7 @@
 import android.provider.Settings;
 import android.server.ServerProtoEnums;
 import android.sysprop.VoldProperties;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.style.SuggestionSpan;
@@ -318,12 +319,12 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.MemInfoReader;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
 import com.android.server.AlarmManagerInternal;
@@ -2175,10 +2176,13 @@
             @Override
             public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
                     boolean asProto) {
-                if (asProto) return;
                 if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                         "cpuinfo", pw)) return;
                 synchronized (mActivityManagerService.mProcessCpuTracker) {
+                    if (asProto) {
+                        mActivityManagerService.mProcessCpuTracker.dumpProto(fd);
+                        return;
+                    }
                     pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentLoad());
                     pw.print(mActivityManagerService.mProcessCpuTracker.printCurrentState(
                             SystemClock.uptimeMillis()));
@@ -3120,7 +3124,7 @@
 
     private boolean hasUsageStatsPermission(String callingPackage) {
         final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
-                Binder.getCallingUid(), callingPackage, null);
+                Binder.getCallingUid(), callingPackage, null, false, "");
         if (mode == AppOpsManager.MODE_DEFAULT) {
             return checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
                     == PackageManager.PERMISSION_GRANTED;
@@ -4337,6 +4341,18 @@
         final boolean allUids = mAtmInternal.isGetTasksAllowed(
                 "getProcessMemoryInfo", callingPid, callingUid);
 
+        // Check if the caller is actually instrumented and from shell, if it's true, we may lift
+        // the throttle of PSS info sampling.
+        boolean isCallerInstrumentedFromShell = false;
+        synchronized (mPidsSelfLocked) {
+            ProcessRecord caller = mPidsSelfLocked.get(callingPid);
+            if (caller != null) {
+                final ActiveInstrumentation instr = caller.getActiveInstrumentation();
+                isCallerInstrumentedFromShell = instr != null
+                        && (instr.mSourceUid == SHELL_UID || instr.mSourceUid == ROOT_UID);
+            }
+        }
+
         Debug.MemoryInfo[] infos = new Debug.MemoryInfo[pids.length];
         for (int i=pids.length-1; i>=0; i--) {
             infos[i] = new Debug.MemoryInfo();
@@ -4360,7 +4376,8 @@
                     continue; // Not allowed to see other users.
                 }
             }
-            if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null) {
+            if (proc != null && proc.lastMemInfoTime >= lastNow && proc.lastMemInfo != null
+                    && !isCallerInstrumentedFromShell) {
                 // It hasn't been long enough that we want to take another sample; return
                 // the last one.
                 infos[i].set(proc.lastMemInfo);
@@ -5816,7 +5833,8 @@
         public int noteOp(String op, int uid, String packageName) {
             // TODO moltmann: Allow to specify featureId
             return mActivityManagerService.mAppOpsService
-                    .noteOperation(AppOpsManager.strOpToOp(op), uid, packageName, null);
+                    .noteOperation(AppOpsManager.strOpToOp(op), uid, packageName, null,
+                            false, "");
         }
 
         @Override
@@ -5968,7 +5986,7 @@
         }
         // ...and legacy apps get an AppOp check
         int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
-                uid, packageName, null);
+                uid, packageName, null, false, "");
         if (DEBUG_BACKGROUND_CHECK) {
             Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
         }
@@ -12592,7 +12610,7 @@
             ArrayList<ProcessRecord> procs, PrintWriter categoryPw) {
         long uptime = SystemClock.uptimeMillis();
         long realtime = SystemClock.elapsedRealtime();
-        final long[] tmpLong = new long[1];
+        final long[] tmpLong = new long[3];
 
         if (procs == null) {
             // No Java processes.  Maybe they want to print a native process.
@@ -12625,17 +12643,25 @@
                         for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
                             final ProcessCpuTracker.Stats r = nativeProcs.get(i);
                             final int pid = r.pid;
-                            if (!opts.isCheckinRequest && opts.dumpDetails) {
-                                pw.println("\n** MEMINFO in pid " + pid + " [" + r.baseName + "] **");
-                            }
                             if (mi == null) {
                                 mi = new Debug.MemoryInfo();
                             }
                             if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
-                                Debug.getMemoryInfo(pid, mi);
+                                if (!Debug.getMemoryInfo(pid, mi)) {
+                                    continue;
+                                }
                             } else {
-                                mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
-                                mi.dalvikPrivateDirty = (int)tmpLong[0];
+                                long pss = Debug.getPss(pid, tmpLong, null);
+                                if (pss == 0) {
+                                    continue;
+                                }
+                                mi.nativePss = (int) pss;
+                                mi.nativePrivateDirty = (int) tmpLong[0];
+                                mi.nativeRss = (int) tmpLong[2];
+                            }
+                            if (!opts.isCheckinRequest && opts.dumpDetails) {
+                                pw.println("\n** MEMINFO in pid " + pid + " ["
+                                        + r.baseName + "] **");
                             }
                             ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest,
                                     opts.dumpFullDetails, opts.dumpDalvik, opts.dumpSummaryOnly,
@@ -12713,9 +12739,6 @@
                 hasActivities = r.hasActivities();
             }
             if (thread != null) {
-                if (!opts.isCheckinRequest && opts.dumpDetails) {
-                    pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
-                }
                 if (mi == null) {
                     mi = new Debug.MemoryInfo();
                 }
@@ -12725,17 +12748,26 @@
                 if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
                     reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
                     startTime = SystemClock.currentThreadTimeMillis();
-                    Debug.getMemoryInfo(pid, mi);
+                    if (!Debug.getMemoryInfo(pid, mi)) {
+                        continue;
+                    }
                     endTime = SystemClock.currentThreadTimeMillis();
                     hasSwapPss = mi.hasSwappedOutPss;
                 } else {
                     reportType = ProcessStats.ADD_PSS_EXTERNAL;
                     startTime = SystemClock.currentThreadTimeMillis();
-                    mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
+                    long pss = Debug.getPss(pid, tmpLong, null);
+                    if (pss == 0) {
+                        continue;
+                    }
+                    mi.dalvikPss = (int) pss;
                     endTime = SystemClock.currentThreadTimeMillis();
-                    mi.dalvikPrivateDirty = (int)tmpLong[0];
+                    mi.dalvikPrivateDirty = (int) tmpLong[0];
                     mi.dalvikRss = (int) tmpLong[2];
                 }
+                if (!opts.isCheckinRequest && opts.dumpDetails) {
+                    pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
+                }
                 if (opts.dumpDetails) {
                     if (opts.localOnly) {
                         ActivityThread.dumpMemInfoTable(pw, mi, opts.isCheckinRequest, opts.dumpFullDetails,
@@ -12865,10 +12897,17 @@
                             mi = new Debug.MemoryInfo();
                         }
                         if (!brief && !opts.oomOnly) {
-                            Debug.getMemoryInfo(st.pid, mi);
+                            if (!Debug.getMemoryInfo(st.pid, mi)) {
+                                continue;
+                            }
                         } else {
-                            mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null);
-                            mi.nativePrivateDirty = (int)tmpLong[0];
+                            long pss = Debug.getPss(st.pid, tmpLong, null);
+                            if (pss == 0) {
+                                continue;
+                            }
+                            mi.nativePss = (int) pss;
+                            mi.nativePrivateDirty = (int) tmpLong[0];
+                            mi.nativeRss = (int) tmpLong[2];
                         }
 
                         final long myTotalPss = mi.getTotalPss();
@@ -13172,7 +13211,7 @@
             ArrayList<ProcessRecord> procs) {
         final long uptimeMs = SystemClock.uptimeMillis();
         final long realtimeMs = SystemClock.elapsedRealtime();
-        final long[] tmpLong = new long[1];
+        final long[] tmpLong = new long[3];
 
         if (procs == null) {
             // No Java processes.  Maybe they want to print a native process.
@@ -13207,20 +13246,29 @@
                         for (int i = nativeProcs.size() - 1 ; i >= 0 ; i--) {
                             final ProcessCpuTracker.Stats r = nativeProcs.get(i);
                             final int pid = r.pid;
-                            final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES);
-
-                            proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
-                            proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName);
 
                             if (mi == null) {
                                 mi = new Debug.MemoryInfo();
                             }
                             if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
-                                Debug.getMemoryInfo(pid, mi);
+                                if (!Debug.getMemoryInfo(pid, mi)) {
+                                    continue;
+                                }
                             } else {
-                                mi.dalvikPss = (int)Debug.getPss(pid, tmpLong, null);
-                                mi.dalvikPrivateDirty = (int)tmpLong[0];
+                                long pss = Debug.getPss(pid, tmpLong, null);
+                                if (pss == 0) {
+                                    continue;
+                                }
+                                mi.nativePss = (int) pss;
+                                mi.nativePrivateDirty = (int) tmpLong[0];
+                                mi.nativeRss = (int) tmpLong[2];
                             }
+
+                            final long nToken = proto.start(MemInfoDumpProto.NATIVE_PROCESSES);
+
+                            proto.write(MemInfoDumpProto.ProcessMemory.PID, pid);
+                            proto.write(MemInfoDumpProto.ProcessMemory.PROCESS_NAME, r.baseName);
+
                             ActivityThread.dumpMemInfoTable(proto, mi, opts.dumpDalvik,
                                     opts.dumpSummaryOnly, 0, 0, 0, 0, 0, 0);
 
@@ -13311,13 +13359,19 @@
             if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
                 reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
                 startTime = SystemClock.currentThreadTimeMillis();
-                Debug.getMemoryInfo(pid, mi);
+                if (!Debug.getMemoryInfo(pid, mi)) {
+                    continue;
+                }
                 endTime = SystemClock.currentThreadTimeMillis();
                 hasSwapPss = mi.hasSwappedOutPss;
             } else {
                 reportType = ProcessStats.ADD_PSS_EXTERNAL;
                 startTime = SystemClock.currentThreadTimeMillis();
-                mi.dalvikPss = (int) Debug.getPss(pid, tmpLong, null);
+                long pss = Debug.getPss(pid, tmpLong, null);
+                if (pss == 0) {
+                    continue;
+                }
+                mi.dalvikPss = (int) pss;
                 endTime = SystemClock.currentThreadTimeMillis();
                 mi.dalvikPrivateDirty = (int) tmpLong[0];
                 mi.dalvikRss = (int) tmpLong[2];
@@ -13445,10 +13499,17 @@
                             mi = new Debug.MemoryInfo();
                         }
                         if (!brief && !opts.oomOnly) {
-                            Debug.getMemoryInfo(st.pid, mi);
+                            if (!Debug.getMemoryInfo(st.pid, mi)) {
+                                continue;
+                            }
                         } else {
-                            mi.nativePss = (int)Debug.getPss(st.pid, tmpLong, null);
-                            mi.nativePrivateDirty = (int)tmpLong[0];
+                            long pss = Debug.getPss(st.pid, tmpLong, null);
+                            if (pss == 0) {
+                                continue;
+                            }
+                            mi.nativePss = (int) pss;
+                            mi.nativePrivateDirty = (int) tmpLong[0];
+                            mi.nativeRss = (int) tmpLong[2];
                         }
 
                         final long myTotalPss = mi.getTotalPss();
@@ -15096,7 +15157,7 @@
                 || AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(action)
                 || AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)
                 || LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION.equals(action)
-                || TelephonyIntents.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
+                || TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE.equals(action)
                 || SuggestionSpan.ACTION_SUGGESTION_PICKED.equals(action)
                 || AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION.equals(action)
                 || AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION.equals(action)) {
@@ -16164,6 +16225,7 @@
                     disableTestApiChecks, mountExtStorageFull, abiOverride);
             app.setActiveInstrumentation(activeInstr);
             activeInstr.mFinished = false;
+            activeInstr.mSourceUid = callingUid;
             activeInstr.mRunningProcesses.add(app);
             if (!mActiveInstrumentation.contains(activeInstr)) {
                 mActiveInstrumentation.add(activeInstr);
@@ -18611,6 +18673,11 @@
         }
 
         @Override
+        public void monitor() {
+            ActivityManagerService.this.monitor();
+        }
+
+        @Override
         public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
             synchronized (ActivityManagerService.this) {
                 return ActivityManagerService.this.inputDispatchingTimedOut(
@@ -19253,18 +19320,22 @@
 
         @Override
         public int noteOperation(int code, int uid, @Nullable String packageName,
-                @Nullable String featureId,
-                @NonNull QuadFunction<Integer, Integer, String, String, Integer> superImpl) {
+                @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+                @Nullable String message,
+                @NonNull HexFunction<Integer, Integer, String, String, Boolean, String, Integer>
+                        superImpl) {
             if (uid == mTargetUid && isTargetOp(code)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
                     return mAppOpsService.noteProxyOperation(code, Process.SHELL_UID,
-                            "com.android.shell", null, uid, packageName, featureId);
+                            "com.android.shell", null, uid, packageName, featureId,
+                            shouldCollectAsyncNotedOp, message);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(code, uid, packageName, featureId);
+            return superImpl.apply(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
+                    message);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 79fe610..a4c44fa 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -111,6 +111,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -1785,7 +1786,7 @@
     }
 
     int runGetCurrentUser(PrintWriter pw) throws RemoteException {
-        UserInfo currentUser = Preconditions.checkNotNull(mInterface.getCurrentUser(),
+        UserInfo currentUser = Objects.requireNonNull(mInterface.getCurrentUser(),
                 "Current user not set");
         pw.println(currentUser.id);
         return 0;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index c380726..5e48dcf 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -88,6 +88,11 @@
     private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
 
     /**
+     * The last time that various processes have crashed and shown an error dialog.
+     */
+    private final ProcessMap<Long> mProcessCrashShowDialogTimes = new ProcessMap<>();
+
+    /**
      * Set of applications that we consider to be bad, and will reject
      * incoming broadcasts from (which the user has no control over).
      * Processes are added to this set when they have crashed twice within
@@ -820,6 +825,11 @@
                 }
                 return;
             }
+            Long crashShowErrorTime = null;
+            if (!proc.isolated) {
+                crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.info.processName,
+                        proc.uid);
+            }
             final boolean showFirstCrash = Settings.Global.getInt(
                     mContext.getContentResolver(),
                     Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
@@ -830,10 +840,16 @@
                     mService.mUserController.getCurrentUserId()) != 0;
             final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
+            final long now = SystemClock.uptimeMillis();
+            final boolean shouldThottle = crashShowErrorTime != null
+                    && now < crashShowErrorTime + ProcessList.MIN_CRASH_INTERVAL;
             if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
-                    && !crashSilenced
+                    && !crashSilenced && !shouldThottle
                     && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
                 proc.getDialogController().showCrashDialogs(data);
+                if (!proc.isolated) {
+                    mProcessCrashShowDialogTimes.put(proc.info.processName, proc.uid, now);
+                }
             } else {
                 // The device is asleep, so just pretend that the user
                 // saw a crash dialog and hit "force quit".
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 10492a7..6fca3f6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -650,7 +650,7 @@
                 // TODO moltmann: Set featureId from caller
                 if (opCode != AppOpsManager.OP_NONE
                         && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
-                                r.callerPackage, null) != AppOpsManager.MODE_ALLOWED) {
+                        r.callerPackage, null, false, "") != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: broadcasting "
                             + r.intent.toString()
                             + " from " + r.callerPackage + " (pid="
@@ -680,10 +680,10 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                // TODO moltmann: Set componentId from caller
+                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                         && mService.mAppOpsService.noteOperation(appOp,
-                        filter.receiverList.uid, filter.packageName, null)
+                        filter.receiverList.uid, filter.packageName, null, false, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent.toString()
@@ -713,10 +713,10 @@
                 skip = true;
             }
         }
-        // TODO moltmann: Set componentId from caller
+        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
                 && mService.mAppOpsService.noteOperation(r.appOp,
-                filter.receiverList.uid, filter.packageName, null)
+                filter.receiverList.uid, filter.packageName, null, false, "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent.toString()
@@ -1370,10 +1370,10 @@
             skip = true;
         } else if (!skip && info.activityInfo.permission != null) {
             final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
-            // TODO moltmann: Set componentId from caller
+            // TODO moltmann: Set featureId from caller
             if (opCode != AppOpsManager.OP_NONE
-                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
-                            r.callerPackage, null) != AppOpsManager.MODE_ALLOWED) {
+                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage,
+                    null, false, "") != AppOpsManager.MODE_ALLOWED) {
                 Slog.w(TAG, "Appop Denial: broadcasting "
                         + r.intent.toString()
                         + " from " + r.callerPackage + " (pid="
@@ -1409,10 +1409,11 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                // TODO moltmann: Set componentId from caller
+                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
                         && mService.mAppOpsService.noteOperation(appOp,
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null)
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null,
+                        false, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent + " to "
@@ -1426,10 +1427,11 @@
                 }
             }
         }
-        // TODO moltmann: Set componentId from caller
+        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
                 && mService.mAppOpsService.noteOperation(r.appOp,
-                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null)
+                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null, false,
+                "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent + " to "
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 12f4656..0fc885a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1541,12 +1541,12 @@
                                         trackedProcState = true;
                                     }
                                 } else if ((cr.flags & Context.BIND_NOT_PERCEPTIBLE) != 0
-                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                        && adj > ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
+                                        && clientAdj <= ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj >= ProcessList.PERCEPTIBLE_LOW_APP_ADJ) {
                                     newAdj = ProcessList.PERCEPTIBLE_LOW_APP_ADJ;
                                 } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
                                         && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                        && adj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
                                     newAdj = ProcessList.PERCEPTIBLE_APP_ADJ;
                                 } else if (clientAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
                                     newAdj = clientAdj;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index f9730a9..eb1ab38 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1723,7 +1723,7 @@
     }
 
     void registerUserSwitchObserver(IUserSwitchObserver observer, String name) {
-        Preconditions.checkNotNull(name, "Observer name cannot be null");
+        Objects.requireNonNull(name, "Observer name cannot be null");
         checkCallingPermission(INTERACT_ACROSS_USERS_FULL, "registerUserSwitchObserver");
         mUserSwitchObservers.register(observer, name);
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 9f23cdaf..a7593c7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -17,8 +17,12 @@
 package com.android.server.appop;
 
 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
+import static android.app.AppOpsManager.HistoricalOpsRequestFilter;
 import static android.app.AppOpsManager.NoteOpEvent;
-import static android.app.AppOpsManager.OpEventProxyInfo;
 import static android.app.AppOpsManager.OP_CAMERA;
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
@@ -26,6 +30,7 @@
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OpEventProxyInfo;
 import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
 import static android.app.AppOpsManager.UID_STATE_CACHED;
 import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
@@ -40,9 +45,12 @@
 import static android.app.AppOpsManager.modeToName;
 import static android.app.AppOpsManager.opToName;
 import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
 
 import android.Manifest;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -50,7 +58,6 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.HistoricalOps;
-import android.app.AppOpsManager.HistoricalOpsRequest;
 import android.app.AppOpsManager.Mode;
 import android.app.AppOpsManager.OpEntry;
 import android.app.AppOpsManager.OpFeatureEntry;
@@ -69,6 +76,8 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
+import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
 import android.database.ContentObserver;
 import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
 import android.net.Uri;
@@ -98,6 +107,7 @@
 import android.util.KeyValueListParser;
 import android.util.LongSparseArray;
 import android.util.Pair;
+import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -122,6 +132,7 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
+import com.android.server.SystemServerInitThreadPool;
 
 import libcore.util.EmptyArray;
 
@@ -195,16 +206,26 @@
     };
 
     private static final int MAX_UNFORWARED_OPS = 10;
+    private static final int MAX_UNUSED_POOLED_OBJECTS = 3;
 
     Context mContext;
     final AtomicFile mFile;
     final Handler mHandler;
 
+    /** Pool for {@link OpEventProxyInfoPool} to avoid to constantly reallocate new objects */
+    @GuardedBy("this")
+    private final OpEventProxyInfoPool mOpEventProxyInfoPool = new OpEventProxyInfoPool();
+
+    /** Pool for {@link InProgressStartOpEventPool} to avoid to constantly reallocate new objects */
+    @GuardedBy("this")
+    private final InProgressStartOpEventPool mInProgressStartOpEventPool =
+            new InProgressStartOpEventPool();
+
     private final AppOpsManagerInternalImpl mAppOpsManagerInternal
             = new AppOpsManagerInternalImpl();
 
     /**
-     * Registered callbacks, called from {@link #noteAsyncOp}.
+     * Registered callbacks, called from {@link #collectAsyncNotedOp}.
      *
      * <p>(package name, uid) -> callbacks
      *
@@ -215,7 +236,7 @@
             mAsyncOpWatchers = new ArrayMap<>();
 
     /**
-     * Async note-ops collected from {@link #noteAsyncOp} that have not been delivered to a
+     * Async note-ops collected from {@link #collectAsyncNotedOp} that have not been delivered to a
      * callback yet.
      *
      * <p>(package name, uid) -> list&lt;ops&gt;
@@ -266,6 +287,46 @@
     private SparseArray<List<Integer>> mSwitchOpToOps;
 
     /**
+     * An unsynchronized pool of {@link OpEventProxyInfo} objects.
+     */
+    private class OpEventProxyInfoPool extends SimplePool<OpEventProxyInfo> {
+        OpEventProxyInfoPool() {
+            super(MAX_UNUSED_POOLED_OBJECTS);
+        }
+
+        OpEventProxyInfo acquire(@IntRange(from = 0) int uid, @Nullable String packageName,
+                @Nullable String featureId) {
+            OpEventProxyInfo recycled = acquire();
+            if (recycled != null) {
+                recycled.reinit(uid, packageName, featureId);
+                return recycled;
+            }
+
+            return new OpEventProxyInfo(uid, packageName, featureId);
+        }
+    }
+
+    /**
+     * An unsynchronized pool of {@link InProgressStartOpEvent} objects.
+     */
+    private class InProgressStartOpEventPool extends SimplePool<InProgressStartOpEvent> {
+        InProgressStartOpEventPool() {
+            super(MAX_UNUSED_POOLED_OBJECTS);
+        }
+
+        InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
+                @NonNull Runnable onDeath, int uidState) throws RemoteException {
+            InProgressStartOpEvent recycled = acquire();
+            if (recycled != null) {
+                recycled.reinit(startTime, elapsedTime, clientId, onDeath, uidState);
+                return recycled;
+            }
+
+            return new InProgressStartOpEvent(startTime, elapsedTime, clientId, onDeath, uidState);
+        }
+    }
+
+    /**
      * All times are in milliseconds. These constants are kept synchronized with the system
      * global Settings. Any access to this class or its fields should be done while
      * holding the AppOpsService lock.
@@ -468,6 +529,9 @@
         final UidState uidState;
         final boolean isPrivileged;
 
+        /** Lazily populated cache of featureIds of this package */
+        final @NonNull ArraySet<String> knownFeatureIds = new ArraySet<>();
+
         Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
             packageName = _packageName;
             uidState = _uidState;
@@ -477,43 +541,102 @@
 
     /** A in progress startOp->finishOp event */
     private static final class InProgressStartOpEvent implements IBinder.DeathRecipient {
-        /** Time of startOp event */
-        final long startTime;
+        /** Wall clock time of startOp event (not monotonic) */
+        private long mStartTime;
+
+        /** Elapsed time since boot of startOp event */
+        private long mStartElapsedTime;
 
         /** Id of the client that started the event */
-        final @NonNull IBinder clientId;
+        private @NonNull IBinder mClientId;
 
         /** To call when client dies */
-        final @NonNull Runnable onDeath;
+        private @NonNull Runnable mOnDeath;
 
         /** uidstate used when calling startOp */
-        final @AppOpsManager.UidState int uidState;
+        private @AppOpsManager.UidState int mUidState;
 
         /** How many times the op was started but not finished yet */
         int numUnfinishedStarts;
 
-        private InProgressStartOpEvent(long startTime, @NonNull IBinder clientId,
-                @NonNull Runnable onDeath, int uidState) throws RemoteException {
-            this.startTime = startTime;
-            this.clientId = clientId;
-            this.onDeath = onDeath;
-            this.uidState = uidState;
+        /**
+         * Create a new {@link InProgressStartOpEvent}.
+         *
+         * @param startTime The time {@link #startOperation} was called
+         * @param startElapsedTime The elapsed time when {@link #startOperation} was called
+         * @param clientId The client id of the caller of {@link #startOperation}
+         * @param onDeath The code to execute on client death
+         * @param uidState The uidstate of the app {@link #startOperation} was called for
+         *
+         * @throws RemoteException If the client is dying
+         */
+        private InProgressStartOpEvent(long startTime, long startElapsedTime,
+                @NonNull IBinder clientId, @NonNull Runnable onDeath, int uidState)
+                throws RemoteException {
+            mStartTime = startTime;
+            mStartElapsedTime = startElapsedTime;
+            mClientId = clientId;
+            mOnDeath = onDeath;
+            mUidState = uidState;
 
             clientId.linkToDeath(this, 0);
         }
 
         /** Clean up event */
         public void finish() {
-            clientId.unlinkToDeath(this, 0);
+            mClientId.unlinkToDeath(this, 0);
         }
 
         @Override
         public void binderDied() {
-            onDeath.run();
+            mOnDeath.run();
+        }
+
+        /**
+         * Reinit existing object with new state.
+         *
+         * @param startTime The time {@link #startOperation} was called
+         * @param startElapsedTime The elapsed time when {@link #startOperation} was called
+         * @param clientId The client id of the caller of {@link #startOperation}
+         * @param onDeath The code to execute on client death
+         * @param uidState The uidstate of the app {@link #startOperation} was called for
+         *
+         * @throws RemoteException If the client is dying
+         */
+        public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
+                @NonNull Runnable onDeath, int uidState) throws RemoteException {
+            mStartTime = startTime;
+            mStartElapsedTime = startElapsedTime;
+            mClientId = clientId;
+            mOnDeath = onDeath;
+            mUidState = uidState;
+
+            clientId.linkToDeath(this, 0);
+        }
+
+        /** @return Wall clock time of startOp event */
+        public long getStartTime() {
+            return mStartTime;
+        }
+
+        /** @return Elapsed time since boot of startOp event */
+        public long getStartElapsedTime() {
+            return mStartElapsedTime;
+        }
+
+        /** @return Id of the client that started the event */
+        public @NonNull IBinder getClientId() {
+            return mClientId;
+        }
+
+        /** @return uidstate used when calling startOp */
+        public int getUidState() {
+            return mUidState;
         }
     }
 
     private final class FeatureOp {
+        public final @Nullable String featureId;
         public final @NonNull Op parent;
 
         /**
@@ -522,7 +645,7 @@
          * <p>Key is {@link AppOpsManager#makeKey}
          */
         @GuardedBy("AppOpsService.this")
-        private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mAccessEvents;
+        private @Nullable LongSparseArray<NoteOpEvent> mAccessEvents;
 
         /**
          * Last rejected accesses for each uidState/opFlag combination
@@ -530,7 +653,7 @@
          * <p>Key is {@link AppOpsManager#makeKey}
          */
         @GuardedBy("AppOpsService.this")
-        private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mRejectEvents;
+        private @Nullable LongSparseArray<NoteOpEvent> mRejectEvents;
 
         /**
          * Currently in progress startOp events
@@ -540,7 +663,8 @@
         @GuardedBy("AppOpsService.this")
         private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
 
-        FeatureOp(@NonNull Op parent) {
+        FeatureOp(@Nullable String featureId, @NonNull Op parent) {
+            this.featureId = featureId;
             this.parent = parent;
         }
 
@@ -558,6 +682,9 @@
                 @OpFlags int flags) {
             accessed(System.currentTimeMillis(), -1, proxyUid, proxyPackageName,
                     proxyFeatureId, uidState, flags);
+
+            mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
+                    featureId, uidState, flags);
         }
 
         /**
@@ -582,9 +709,16 @@
 
             OpEventProxyInfo proxyInfo = null;
             if (proxyUid != Process.INVALID_UID) {
-                proxyInfo = new OpEventProxyInfo(proxyUid, proxyPackageName, proxyFeatureId);
+                proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+                        proxyFeatureId);
             }
-            mAccessEvents.put(key, new NoteOpEvent(noteTime, duration, proxyInfo));
+
+            NoteOpEvent existingEvent = mAccessEvents.get(key);
+            if (existingEvent != null) {
+                existingEvent.reinit(noteTime, duration, proxyInfo, mOpEventProxyInfoPool);
+            } else {
+                mAccessEvents.put(key, new NoteOpEvent(noteTime, duration, proxyInfo));
+            }
         }
 
         /**
@@ -595,6 +729,9 @@
          */
         public void rejected(@AppOpsManager.UidState int uidState, @OpFlags int flags) {
             rejected(System.currentTimeMillis(), uidState, flags);
+
+            mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName,
+                    featureId, uidState, flags);
         }
 
         /**
@@ -613,7 +750,12 @@
             }
 
             // We do not collect proxy information for rejections yet
-            mRejectEvents.put(key, new NoteOpEvent(noteTime, -1, null));
+            NoteOpEvent existingEvent = mRejectEvents.get(key);
+            if (existingEvent != null) {
+                existingEvent.reinit(noteTime, -1, null, mOpEventProxyInfoPool);
+            } else {
+                mRejectEvents.put(key, new NoteOpEvent(noteTime, -1, null));
+            }
         }
 
         /**
@@ -633,27 +775,15 @@
                 mInProgressEvents = new ArrayMap<>(1);
             }
 
-
             InProgressStartOpEvent event = mInProgressEvents.get(clientId);
             if (event == null) {
-                event = new InProgressStartOpEvent(System.currentTimeMillis(), clientId, () -> {
-                    // In the case the client dies without calling finish first
-                    synchronized (AppOpsService.this) {
-                        if (mInProgressEvents == null) {
-                            return;
-                        }
-
-                        InProgressStartOpEvent deadEvent = mInProgressEvents.get(clientId);
-                        if (deadEvent != null) {
-                            deadEvent.numUnfinishedStarts = 1;
-                        }
-
-                        finished(clientId);
-                    }
-                }, uidState);
+                event = mInProgressStartOpEventPool.acquire(System.currentTimeMillis(),
+                        SystemClock.elapsedRealtime(), clientId,
+                        PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
+                        uidState);
                 mInProgressEvents.put(clientId, event);
             } else {
-                if (uidState != event.uidState) {
+                if (uidState != event.mUidState) {
                     onUidStateChanged(uidState);
                 }
             }
@@ -662,7 +792,7 @@
 
             // startOp events don't support proxy, hence use flags==SELF
             mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
-                    uidState, OP_FLAG_SELF);
+                    featureId, uidState, OP_FLAG_SELF);
         }
 
         /**
@@ -697,13 +827,15 @@
                 }
 
                 // startOp events don't support proxy, hence use flags==SELF
-                NoteOpEvent finishedEvent = new NoteOpEvent(event.startTime,
-                        System.currentTimeMillis() - event.startTime, null);
-                mAccessEvents.put(makeKey(event.uidState, OP_FLAG_SELF), finishedEvent);
+                NoteOpEvent finishedEvent = new NoteOpEvent(event.getStartTime(),
+                        SystemClock.elapsedRealtime() - event.getStartElapsedTime(), null);
+                mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent);
 
                 mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
-                        parent.packageName, event.uidState, AppOpsManager.OP_FLAG_SELF,
-                        finishedEvent.duration);
+                        parent.packageName, featureId, event.getUidState(),
+                        AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration());
+
+                mInProgressStartOpEventPool.release(event);
 
                 if (mInProgressEvents.isEmpty()) {
                     mInProgressEvents = null;
@@ -718,6 +850,26 @@
         }
 
         /**
+         * Called in the case the client dies without calling finish first
+         *
+         * @param clientId The client that died
+         */
+        void onClientDeath(@NonNull IBinder clientId) {
+            synchronized (AppOpsService.this) {
+                if (mInProgressEvents == null) {
+                    return;
+                }
+
+                InProgressStartOpEvent deadEvent = mInProgressEvents.get(clientId);
+                if (deadEvent != null) {
+                    deadEvent.numUnfinishedStarts = 1;
+                }
+
+                finished(clientId);
+            }
+        }
+
+        /**
          * Notify that the state of the uid changed
          *
          * @param newState The new state
@@ -731,10 +883,10 @@
             for (int i = 0; i < numInProgressEvents; i++) {
                 InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
 
-                if (event.uidState != newState) {
+                if (event.getUidState() != newState) {
                     try {
-                        finished(event.clientId, false);
-                        started(event.clientId, newState);
+                        finished(event.getClientId(), false);
+                        started(event.getClientId(), newState);
                     } catch (RemoteException e) {
                         if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
                     }
@@ -742,6 +894,59 @@
             }
         }
 
+        /**
+         * Combine {@code a} and {@code b} and return the result. The result might be {@code a}
+         * or {@code b}. If there is an event for the same key in both the later event is retained.
+         */
+        private @Nullable LongSparseArray<NoteOpEvent> add(@Nullable LongSparseArray<NoteOpEvent> a,
+                @Nullable LongSparseArray<NoteOpEvent> b) {
+            if (a == null) {
+                return b;
+            }
+
+            if (b == null) {
+                return a;
+            }
+
+            int numEventsToAdd = b.size();
+            for (int i = 0; i < numEventsToAdd; i++) {
+                long keyOfEventToAdd = b.keyAt(i);
+                NoteOpEvent bEvent = b.valueAt(i);
+                NoteOpEvent aEvent = a.get(keyOfEventToAdd);
+
+                if (aEvent == null || bEvent.getNoteTime() > aEvent.getNoteTime()) {
+                    a.put(keyOfEventToAdd, bEvent);
+                }
+            }
+
+            return a;
+        }
+
+        /**
+         * Add all data from the {@code featureToAdd} to this op.
+         *
+         * <p>If there is an event for the same key in both the later event is retained.
+         * <p>{@code opToAdd} should not be used after this method is called.
+         *
+         * @param opToAdd The op to add
+         */
+        public void add(@NonNull FeatureOp opToAdd) {
+            if (opToAdd.mInProgressEvents != null) {
+                Slog.w(TAG, "Ignoring " + opToAdd.mInProgressEvents.size() + " running app-ops");
+
+                int numInProgressEvents = opToAdd.mInProgressEvents.size();
+                for (int i = 0; i < numInProgressEvents; i++) {
+                    InProgressStartOpEvent event = opToAdd.mInProgressEvents.valueAt(i);
+
+                    event.finish();
+                    mInProgressStartOpEventPool.release(event);
+                }
+            }
+
+            mAccessEvents = add(mAccessEvents, opToAdd.mAccessEvents);
+            mRejectEvents = add(mRejectEvents, opToAdd.mRejectEvents);
+        }
+
         public boolean isRunning() {
             return mInProgressEvents != null;
         }
@@ -751,15 +956,30 @@
                     || (mRejectEvents != null && mRejectEvents.size() > 0);
         }
 
-        @NonNull OpFeatureEntry createFeatureEntryLocked() {
-            LongSparseArray<NoteOpEvent> accessEvents = null;
-            if (mAccessEvents != null) {
-                accessEvents = mAccessEvents.clone();
+        /**
+         * Clone a {@link LongSparseArray} and clone all values.
+         */
+        private @Nullable LongSparseArray<NoteOpEvent> deepClone(
+                @Nullable LongSparseArray<NoteOpEvent> original) {
+            if (original == null) {
+                return original;
             }
 
+            int size = original.size();
+            LongSparseArray<NoteOpEvent> clone = new LongSparseArray<>(size);
+            for (int i = 0; i < size; i++) {
+                clone.put(original.keyAt(i), new NoteOpEvent(original.valueAt(i)));
+            }
+
+            return clone;
+        }
+
+        @NonNull OpFeatureEntry createFeatureEntryLocked() {
+            LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents);
+
             // Add in progress events as access events
             if (mInProgressEvents != null) {
-                long now = System.currentTimeMillis();
+                long now = SystemClock.elapsedRealtime();
                 int numInProgressEvents = mInProgressEvents.size();
 
                 if (accessEvents == null) {
@@ -770,15 +990,13 @@
                     InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
 
                     // startOp events don't support proxy
-                    accessEvents.append(makeKey(event.uidState, OP_FLAG_SELF),
-                            new NoteOpEvent(event.startTime, now - event.startTime, null));
+                    accessEvents.append(makeKey(event.getUidState(), OP_FLAG_SELF),
+                            new NoteOpEvent(event.getStartTime(), now - event.getStartElapsedTime(),
+                                    null));
                 }
             }
 
-            LongSparseArray<NoteOpEvent> rejectEvents = null;
-            if (mRejectEvents != null) {
-                rejectEvents = mRejectEvents.clone();
-            }
+            LongSparseArray<NoteOpEvent> rejectEvents = deepClone(mRejectEvents);
 
             return new OpFeatureEntry(parent.op, isRunning(), accessEvents, rejectEvents);
         }
@@ -825,7 +1043,7 @@
 
             featureOp = mFeatures.get(featureId);
             if (featureOp == null) {
-                featureOp = new FeatureOp(parent);
+                featureOp = new FeatureOp(featureId, parent);
                 mFeatures.put(featureId, featureOp);
             }
 
@@ -1018,6 +1236,13 @@
         }
     }
 
+    /**
+     * Call {@link FeatureOp#onClientDeath featureOp.onClientDeath(clientId)}.
+     */
+    private static void onClientDeath(@NonNull FeatureOp featureOp, @NonNull IBinder clientId) {
+        featureOp.onClientDeath(clientId);
+    }
+
     public AppOpsService(File storagePath, Handler handler) {
         LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
         mFile = new AtomicFile(storagePath, "appops");
@@ -1032,20 +1257,110 @@
         LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal);
     }
 
+    /** Handler for work when packages are removed or updated */
+    private BroadcastReceiver mOnPackageUpdatedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            String pkgName = intent.getData().getEncodedSchemeSpecificPart();
+            int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
+
+            if (action.equals(ACTION_PACKAGE_REMOVED) && !intent.hasExtra(EXTRA_REPLACING)) {
+                synchronized (AppOpsService.this) {
+                    UidState uidState = mUidStates.get(uid);
+                    if (uidState == null || uidState.pkgOps == null) {
+                        return;
+                    }
+
+                    Ops removedOps = uidState.pkgOps.remove(pkgName);
+                    if (removedOps != null) {
+                        scheduleFastWriteLocked();
+                    }
+                }
+            } else if (action.equals(Intent.ACTION_PACKAGE_REPLACED)) {
+                AndroidPackage pkg = LocalServices.getService(
+                        PackageManagerInternal.class).getPackage(pkgName);
+                if (pkg == null) {
+                    return;
+                }
+
+                ArrayMap<String, String> dstFeatureIds = new ArrayMap<>();
+                ArraySet<String> featureIds = new ArraySet<>();
+                if (pkg.getFeatures() != null) {
+                    int numFeatures = pkg.getFeatures().size();
+                    for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
+                        ParsedFeature feature = pkg.getFeatures().get(featureNum);
+                        featureIds.add(feature.id);
+
+                        int numInheritFrom = feature.inheritFrom.size();
+                        for (int inheritFromNum = 0; inheritFromNum < numInheritFrom;
+                                inheritFromNum++) {
+                            dstFeatureIds.put(feature.inheritFrom.get(inheritFromNum),
+                                    feature.id);
+                        }
+                    }
+                }
+
+                synchronized (AppOpsService.this) {
+                    UidState uidState = mUidStates.get(uid);
+                    if (uidState == null || uidState.pkgOps == null) {
+                        return;
+                    }
+
+                    Ops ops = uidState.pkgOps.get(pkgName);
+                    if (ops == null) {
+                        return;
+                    }
+
+                    ops.knownFeatureIds.clear();
+                    int numOps = ops.size();
+                    for (int opNum = 0; opNum < numOps; opNum++) {
+                        Op op = ops.valueAt(opNum);
+
+                        int numFeatures = op.mFeatures.size();
+                        for (int featureNum = numFeatures - 1; featureNum >= 0; featureNum--) {
+                            String featureId = op.mFeatures.keyAt(featureNum);
+
+                            if (featureIds.contains(featureId)) {
+                                // feature still exist after upgrade
+                                continue;
+                            }
+
+                            String newFeatureId = dstFeatureIds.get(featureId);
+
+                            FeatureOp newFeatureOp = op.getOrCreateFeature(op, newFeatureId);
+                            newFeatureOp.add(op.mFeatures.valueAt(featureNum));
+                            op.mFeatures.removeAt(featureNum);
+
+                            scheduleFastWriteLocked();
+                        }
+                    }
+                }
+            }
+        }
+    };
+
     public void systemReady() {
         mConstants.startMonitoring(mContext.getContentResolver());
         mHistoricalRegistry.systemReady(mContext.getContentResolver());
 
-        synchronized (this) {
-            boolean changed = false;
-            for (int i = mUidStates.size() - 1; i >= 0; i--) {
-                UidState uidState = mUidStates.valueAt(i);
+        IntentFilter packageUpdateFilter = new IntentFilter();
+        packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        packageUpdateFilter.addDataScheme("package");
 
-                String[] packageNames = getPackagesForUid(uidState.uid);
-                if (ArrayUtils.isEmpty(packageNames)) {
+        mContext.registerReceiver(mOnPackageUpdatedReceiver, packageUpdateFilter);
+
+        synchronized (this) {
+            for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) {
+                int uid = mUidStates.keyAt(uidNum);
+                UidState uidState = mUidStates.valueAt(uidNum);
+
+                String[] pkgsInUid = getPackagesForUid(uidState.uid);
+                if (ArrayUtils.isEmpty(pkgsInUid)) {
                     uidState.clear();
-                    mUidStates.removeAt(i);
-                    changed = true;
+                    mUidStates.removeAt(uidNum);
+                    scheduleFastWriteLocked();
                     continue;
                 }
 
@@ -1054,31 +1369,24 @@
                     continue;
                 }
 
-                Iterator<Ops> it = pkgs.values().iterator();
-                while (it.hasNext()) {
-                    Ops ops = it.next();
-                    int curUid = -1;
-                    try {
-                        curUid = AppGlobals.getPackageManager().getPackageUid(ops.packageName,
-                                PackageManager.MATCH_UNINSTALLED_PACKAGES,
-                                UserHandle.getUserId(ops.uidState.uid));
-                    } catch (RemoteException ignored) {
-                    }
-                    if (curUid != ops.uidState.uid) {
-                        Slog.i(TAG, "Pruning old package " + ops.packageName
-                                + "/" + ops.uidState + ": new uid=" + curUid);
-                        it.remove();
-                        changed = true;
-                    }
-                }
+                int numPkgs = pkgs.size();
+                for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+                    String pkg = pkgs.keyAt(pkgNum);
 
-                if (uidState.isDefault()) {
-                    mUidStates.removeAt(i);
+                    String action;
+                    if (!ArrayUtils.contains(pkgsInUid, pkg)) {
+                        action = Intent.ACTION_PACKAGE_REMOVED;
+                    } else {
+                        action = Intent.ACTION_PACKAGE_REPLACED;
+                    }
+
+                    SystemServerInitThreadPool.submit(
+                            () -> mOnPackageUpdatedReceiver.onReceive(mContext, new Intent(action)
+                                    .setData(Uri.fromParts("package", pkg, null))
+                                    .putExtra(Intent.EXTRA_UID, uid)),
+                            "Update app-ops uidState in case package " + pkg + " changed");
                 }
             }
-            if (changed) {
-                scheduleFastWriteLocked();
-            }
         }
 
         final IntentFilter packageSuspendFilter = new IntentFilter();
@@ -1140,11 +1448,13 @@
                                 return Zygote.MOUNT_EXTERNAL_NONE;
                             }
                             if (noteOperation(AppOpsManager.OP_READ_EXTERNAL_STORAGE, uid,
-                                    packageName, null) != AppOpsManager.MODE_ALLOWED) {
+                                    packageName, null, true, "External storage policy")
+                                    != AppOpsManager.MODE_ALLOWED) {
                                 return Zygote.MOUNT_EXTERNAL_NONE;
                             }
                             if (noteOperation(AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, uid,
-                                    packageName, null) != AppOpsManager.MODE_ALLOWED) {
+                                    packageName, null, true, "External storage policy")
+                                    != AppOpsManager.MODE_ALLOWED) {
                                 return Zygote.MOUNT_EXTERNAL_READ;
                             }
                             return Zygote.MOUNT_EXTERNAL_WRITE;
@@ -1192,7 +1502,7 @@
                         FeatureOp featureOp = op.mFeatures.valueAt(featureNum);
 
                         while (featureOp.mInProgressEvents != null) {
-                            featureOp.mInProgressEvents.valueAt(0).onDeath.run();
+                            featureOp.finished(featureOp.mInProgressEvents.keyAt(0));
                         }
                     }
                 }
@@ -1382,7 +1692,7 @@
             return Collections.emptyList();
         }
         synchronized (this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false /* isPrivileged */,
+            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false /* isPrivileged */,
                     false /* edit */);
             if (pkgOps == null) {
                 return null;
@@ -1399,19 +1709,48 @@
         }
     }
 
+    /**
+     * Verify that historical appop request arguments are valid.
+     */
+    private void ensureHistoricalOpRequestIsValid(int uid, String packageName, String featureId,
+            List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
+            int flags) {
+        if ((filter & FILTER_BY_UID) != 0) {
+            Preconditions.checkArgument(uid != Process.INVALID_UID);
+        } else {
+            Preconditions.checkArgument(uid == Process.INVALID_UID);
+        }
+
+        if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
+            Objects.requireNonNull(packageName);
+        } else {
+            Preconditions.checkArgument(packageName == null);
+        }
+
+        if ((filter & FILTER_BY_FEATURE_ID) == 0) {
+            Preconditions.checkArgument(featureId == null);
+        }
+
+        if ((filter & FILTER_BY_OP_NAMES) != 0) {
+            Objects.requireNonNull(opNames);
+        } else {
+            Preconditions.checkArgument(opNames == null);
+        }
+
+        Preconditions.checkFlagsArgument(filter,
+                FILTER_BY_UID | FILTER_BY_PACKAGE_NAME | FILTER_BY_FEATURE_ID | FILTER_BY_OP_NAMES);
+        Preconditions.checkArgumentNonnegative(beginTimeMillis);
+        Preconditions.checkArgument(endTimeMillis > beginTimeMillis);
+        Preconditions.checkFlagsArgument(flags, OP_FLAGS_ALL);
+    }
+
     @Override
-    public void getHistoricalOps(int uid, @NonNull String packageName,
-            @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
-            @OpFlags int flags, @NonNull RemoteCallback callback) {
-        // Use the builder to validate arguments.
-        new HistoricalOpsRequest.Builder(
-                beginTimeMillis, endTimeMillis)
-                .setUid(uid)
-                .setPackageName(packageName)
-                .setOpNames(opNames)
-                .setFlags(flags)
-                .build();
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+    public void getHistoricalOps(int uid, String packageName, String featureId,
+            List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
+            int flags, RemoteCallback callback) {
+        ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+                beginTimeMillis, endTimeMillis, flags);
+        Objects.requireNonNull(callback, "callback cannot be null");
 
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
@@ -1420,23 +1759,17 @@
                 ? opNames.toArray(new String[opNames.size()]) : null;
 
         // Must not hold the appops lock
-        mHistoricalRegistry.getHistoricalOps(uid, packageName, opNamesArray,
+        mHistoricalRegistry.getHistoricalOps(uid, packageName, featureId, opNamesArray, filter,
                 beginTimeMillis, endTimeMillis, flags, callback);
     }
 
     @Override
-    public void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
-            @Nullable List<String> opNames, long beginTimeMillis, long endTimeMillis,
-            @OpFlags int flags, @NonNull RemoteCallback callback) {
-        // Use the builder to validate arguments.
-        new HistoricalOpsRequest.Builder(
-                beginTimeMillis, endTimeMillis)
-                .setUid(uid)
-                .setPackageName(packageName)
-                .setOpNames(opNames)
-                .setFlags(flags)
-                .build();
-        Preconditions.checkNotNull(callback, "callback cannot be null");
+    public void getHistoricalOpsFromDiskRaw(int uid, String packageName, String featureId,
+            List<String> opNames, int filter, long beginTimeMillis, long endTimeMillis,
+            int flags, RemoteCallback callback) {
+        ensureHistoricalOpRequestIsValid(uid, packageName, featureId, opNames, filter,
+                beginTimeMillis, endTimeMillis, flags);
+        Objects.requireNonNull(callback, "callback cannot be null");
 
         mContext.enforcePermission(Manifest.permission.MANAGE_APPOPS,
                 Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps");
@@ -1445,8 +1778,8 @@
                 ? opNames.toArray(new String[opNames.size()]) : null;
 
         // Must not hold the appops lock
-        mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, opNamesArray,
-                beginTimeMillis, endTimeMillis, flags, callback);
+        mHistoricalRegistry.getHistoricalOpsFromDiskRaw(uid, packageName, featureId, opNamesArray,
+                filter, beginTimeMillis, endTimeMillis, flags, callback);
     }
 
     @Override
@@ -1482,7 +1815,7 @@
         op.removeFeaturesWithNoTime();
 
         if (op.mFeatures.size() == 0) {
-            Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */,
+            Ops ops = getOpsRawLocked(uid, packageName, null, false /* isPrivileged */,
                     false /* edit */);
             if (ops != null) {
                 ops.remove(op.op);
@@ -1746,7 +2079,7 @@
 
         boolean isPrivileged;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null);
         } catch (SecurityException e) {
             Slog.e(TAG, "Cannot setMode", e);
             return;
@@ -1761,7 +2094,7 @@
 
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
-            Op op = getOpLocked(code, uid, packageName, isPrivileged, true);
+            Op op = getOpLocked(code, uid, packageName, null, isPrivileged, true);
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
@@ -2131,7 +2464,7 @@
         boolean isPrivileged;
 
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, null);
         } catch (SecurityException e) {
             Slog.e(TAG, "checkOperation", e);
             return AppOpsManager.opToDefaultMode(code);
@@ -2141,7 +2474,7 @@
             return AppOpsManager.MODE_IGNORED;
         }
         synchronized (this) {
-            if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, packageName, null, isPrivileged)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             code = AppOpsManager.opToSwitch(code);
@@ -2151,7 +2484,7 @@
                 final int rawMode = uidState.opModes.get(code);
                 return raw ? rawMode : uidState.evalMode(code, rawMode);
             }
-            Op op = getOpLocked(code, uid, packageName, false, false);
+            Op op = getOpLocked(code, uid, packageName, null, false, false);
             if (op == null) {
                 return AppOpsManager.opToDefaultMode(code);
             }
@@ -2212,9 +2545,9 @@
 
     @Override
     public int checkPackage(int uid, String packageName) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         try {
-            verifyAndGetIsPrivileged(uid, packageName);
+            verifyAndGetIsPrivileged(uid, packageName, null);
 
             return AppOpsManager.MODE_ALLOWED;
         } catch (SecurityException ignored) {
@@ -2225,7 +2558,7 @@
     @Override
     public int noteProxyOperation(int code, int proxiedUid, String proxiedPackageName,
             String proxiedFeatureId, int proxyUid, String proxyPackageName,
-            String proxyFeatureId) {
+            String proxyFeatureId, boolean shouldCollectAsyncNotedOp, String message) {
         verifyIncomingUid(proxyUid);
         verifyIncomingOp(code);
 
@@ -2241,7 +2574,8 @@
         final int proxyFlags = isProxyTrusted ? AppOpsManager.OP_FLAG_TRUSTED_PROXY
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXY;
         final int proxyMode = noteOperationUnchecked(code, proxyUid, resolveProxyPackageName,
-                proxyFeatureId, Process.INVALID_UID, null, null, proxyFlags);
+                proxyFeatureId, Process.INVALID_UID, null, null, proxyFlags,
+                !isProxyTrusted, "proxy " + message);
         if (proxyMode != AppOpsManager.MODE_ALLOWED || Binder.getCallingUid() == proxiedUid) {
             return proxyMode;
         }
@@ -2254,23 +2588,27 @@
                 : AppOpsManager.OP_FLAG_UNTRUSTED_PROXIED;
         return noteOperationUnchecked(code, proxiedUid, resolveProxiedPackageName,
                 proxiedFeatureId, proxyUid, resolveProxyPackageName, proxyFeatureId,
-                proxiedFlags);
+                proxiedFlags, shouldCollectAsyncNotedOp, message);
     }
 
     @Override
-    public int noteOperation(int code, int uid, String packageName, String featureId) {
+    public int noteOperation(int code, int uid, String packageName, String featureId,
+            boolean shouldCollectAsyncNotedOp, String message) {
         final CheckOpsDelegate delegate;
         synchronized (this) {
             delegate = mCheckOpsDelegate;
         }
         if (delegate == null) {
-            return noteOperationImpl(code, uid, packageName, featureId);
+            return noteOperationImpl(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
+                    message);
         }
-        return delegate.noteOperation(code, uid, packageName, featureId,
-                AppOpsService.this::noteOperationImpl);
+        return delegate.noteOperation(code, uid, packageName, featureId, shouldCollectAsyncNotedOp,
+                message, AppOpsService.this::noteOperationImpl);
     }
 
-    private int noteOperationImpl(int code, int uid, String packageName, String featureId) {
+    private int noteOperationImpl(int code, int uid, @Nullable String packageName,
+            @Nullable String featureId, boolean shouldCollectAsyncNotedOp,
+            @Nullable String message) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -2278,24 +2616,25 @@
             return AppOpsManager.MODE_IGNORED;
         }
         return noteOperationUnchecked(code, uid, resolvedPackageName, featureId,
-                Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF);
+                Process.INVALID_UID, null, null, AppOpsManager.OP_FLAG_SELF,
+                shouldCollectAsyncNotedOp, message);
     }
 
-    private int noteOperationUnchecked(int code, int uid, String packageName, String featureId,
-            int proxyUid, String proxyPackageName, @Nullable String proxyFeatureId,
-            @OpFlags int flags) {
-        // TODO moltmann: Verify that feature is declared in package
-
+    private int noteOperationUnchecked(int code, int uid, @NonNull String packageName,
+            @Nullable String featureId, int proxyUid, String proxyPackageName,
+            @Nullable String proxyFeatureId, @OpFlags int flags, boolean shouldCollectAsyncNotedOp,
+            @Nullable String message) {
         boolean isPrivileged;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
         } catch (SecurityException e) {
             Slog.e(TAG, "noteOperation", e);
             return AppOpsManager.MODE_ERRORED;
         }
 
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, packageName, isPrivileged, true /* edit */);
+            final Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged,
+                    true /* edit */);
             if (ops == null) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
@@ -2303,9 +2642,9 @@
                         + " package " + packageName);
                 return AppOpsManager.MODE_ERRORED;
             }
-            final Op op = getOpLocked(ops, code, true);
+            final Op op = getOpLocked(ops, code, uid, true);
             final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
-            if (isOpRestrictedLocked(uid, code, packageName, isPrivileged)) {
+            if (isOpRestrictedLocked(uid, code, packageName, featureId, isPrivileged)) {
                 scheduleOpNotedIfNeededLocked(code, uid, packageName,
                         AppOpsManager.MODE_IGNORED);
                 return AppOpsManager.MODE_IGNORED;
@@ -2314,7 +2653,7 @@
             if (featureOp.isRunning()) {
                 Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
                         + code + " startTime of in progress event="
-                        + featureOp.mInProgressEvents.valueAt(0).startTime);
+                        + featureOp.mInProgressEvents.valueAt(0).getStartTime());
             }
 
             final int switchCode = AppOpsManager.opToSwitch(code);
@@ -2327,21 +2666,18 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     featureOp.rejected(uidState.state, flags);
-                    mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
-                            uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, uidMode);
                     return uidMode;
                 }
             } else {
-                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
+                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
+                        : op;
                 final int mode = switchOp.evalMode();
                 if (mode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     featureOp.rejected(uidState.state, flags);
-                    mHistoricalRegistry.incrementOpRejected(code, uid, packageName,
-                            uidState.state, flags);
                     scheduleOpNotedIfNeededLocked(code, uid, packageName, mode);
                     return mode;
                 }
@@ -2349,11 +2685,13 @@
             if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                     + " package " + packageName + (featureId == null ? "" : "." + featureId));
             featureOp.accessed(proxyUid, proxyPackageName, proxyFeatureId, uidState.state, flags);
-            // TODO moltmann: Add features to historical app-ops
-            mHistoricalRegistry.incrementOpAccessedCount(op.op, uid, packageName,
-                    uidState.state, flags);
             scheduleOpNotedIfNeededLocked(code, uid, packageName,
                     AppOpsManager.MODE_ALLOWED);
+
+            if (shouldCollectAsyncNotedOp) {
+                collectAsyncNotedOp(uid, packageName, code, featureId, message);
+            }
+
             return AppOpsManager.MODE_ALLOWED;
         }
     }
@@ -2419,7 +2757,7 @@
         Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
         Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
                 "Invalid op code in: " + Arrays.toString(ops));
-        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        Objects.requireNonNull(callback, "Callback cannot be null");
         synchronized (this) {
             SparseArray<NotedCallback> callbacks = mNotedWatchers.get(callback.asBinder());
             if (callbacks == null) {
@@ -2436,7 +2774,7 @@
 
     @Override
     public void stopWatchingNoted(IAppOpsNotedCallback callback) {
-        Preconditions.checkNotNull(callback, "Callback cannot be null");
+        Objects.requireNonNull(callback, "Callback cannot be null");
         synchronized (this) {
             final SparseArray<NotedCallback> notedCallbacks =
                     mNotedWatchers.remove(callback.asBinder());
@@ -2450,21 +2788,20 @@
         }
     }
 
-    @Override
-    public void noteAsyncOp(String callingPackageName, int uid, String packageName, int opCode,
-            String featureId, String message) {
-        Preconditions.checkNotNull(message);
-        verifyAndGetIsPrivileged(uid, packageName);
-
-        verifyIncomingUid(uid);
-        verifyIncomingOp(opCode);
+    /**
+     * Collect an {@link AsyncNotedAppOp}.
+     *
+     * @param uid The uid the op was noted for
+     * @param packageName The package the op was noted for
+     * @param opCode The code of the op noted
+     * @param featureId The id of the feature to op was noted for
+     * @param message The message for the op noting
+     */
+    private void collectAsyncNotedOp(int uid, @NonNull String packageName, int opCode,
+            @Nullable String featureId, @NonNull String message) {
+        Objects.requireNonNull(message);
 
         int callingUid = Binder.getCallingUid();
-        long now = System.currentTimeMillis();
-
-        if (callingPackageName != null) {
-            verifyAndGetIsPrivileged(callingUid, callingPackageName);
-        }
 
         long token = Binder.clearCallingIdentity();
         try {
@@ -2473,7 +2810,7 @@
 
                 RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
                 AsyncNotedAppOp asyncNotedOp = new AsyncNotedAppOp(opCode, callingUid,
-                        callingPackageName, featureId, message, now);
+                        featureId, message, System.currentTimeMillis());
                 final boolean[] wasNoteForwarded = {false};
 
                 if (callbacks != null) {
@@ -2522,13 +2859,13 @@
 
     @Override
     public void startWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(callback);
 
         int uid = Binder.getCallingUid();
         Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
 
-        verifyAndGetIsPrivileged(uid, packageName);
+        verifyAndGetIsPrivileged(uid, packageName, null);
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -2552,13 +2889,13 @@
 
     @Override
     public void stopWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback) {
-        Preconditions.checkNotNull(packageName);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(callback);
 
         int uid = Binder.getCallingUid();
         Pair<String, Integer> key = getAsyncNotedOpsKey(packageName, uid);
 
-        verifyAndGetIsPrivileged(uid, packageName);
+        verifyAndGetIsPrivileged(uid, packageName, null);
 
         synchronized (this) {
             RemoteCallbackList<IAppOpsAsyncNotedCallback> callbacks = mAsyncOpWatchers.get(key);
@@ -2573,11 +2910,11 @@
 
     @Override
     public List<AsyncNotedAppOp> extractAsyncOps(String packageName) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
 
         int uid = Binder.getCallingUid();
 
-        verifyAndGetIsPrivileged(uid, packageName);
+        verifyAndGetIsPrivileged(uid, packageName, null);
 
         synchronized (this) {
             return mUnforwardedAsyncNotedOps.remove(getAsyncNotedOpsKey(packageName, uid));
@@ -2586,7 +2923,8 @@
 
     @Override
     public int startOperation(IBinder clientId, int code, int uid, String packageName,
-            String featureId, boolean startIfModeDefault) {
+            String featureId, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
+            String message) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -2596,22 +2934,22 @@
 
         boolean isPrivileged;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
         } catch (SecurityException e) {
             Slog.e(TAG, "startOperation", e);
             return AppOpsManager.MODE_ERRORED;
         }
 
         synchronized (this) {
-            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, isPrivileged,
+            final Ops ops = getOpsRawLocked(uid, resolvedPackageName, featureId, isPrivileged,
                     true /* edit */);
             if (ops == null) {
                 if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                         + " package " + resolvedPackageName);
                 return AppOpsManager.MODE_ERRORED;
             }
-            final Op op = getOpLocked(ops, code, true);
-            if (isOpRestrictedLocked(uid, code, resolvedPackageName, isPrivileged)) {
+            final Op op = getOpLocked(ops, code, uid, true);
+            if (isOpRestrictedLocked(uid, code, resolvedPackageName, featureId, isPrivileged)) {
                 return AppOpsManager.MODE_IGNORED;
             }
             final FeatureOp featureOp = op.getOrCreateFeature(op, featureId);
@@ -2628,12 +2966,11 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
                     featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
-                    mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
-                            uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return uidMode;
                 }
             } else {
-                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
+                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
+                        : op;
                 final int mode = switchOp.evalMode();
                 if (mode != AppOpsManager.MODE_ALLOWED
                         && (!startIfModeDefault || mode != AppOpsManager.MODE_DEFAULT)) {
@@ -2641,8 +2978,6 @@
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
                     featureOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
-                    mHistoricalRegistry.incrementOpRejected(opCode, uid, packageName,
-                            uidState.state, AppOpsManager.OP_FLAG_SELF);
                     return mode;
                 }
             }
@@ -2656,6 +2991,10 @@
             }
         }
 
+        if (shouldCollectAsyncNotedOp) {
+            collectAsyncNotedOp(uid, packageName, code, featureId, message);
+        }
+
         return AppOpsManager.MODE_ALLOWED;
     }
 
@@ -2671,14 +3010,14 @@
 
         boolean isPrivileged;
         try {
-            isPrivileged = verifyAndGetIsPrivileged(uid, packageName);
+            isPrivileged = verifyAndGetIsPrivileged(uid, packageName, featureId);
         } catch (SecurityException e) {
             Slog.e(TAG, "Cannot finishOperation", e);
             return;
         }
 
         synchronized (this) {
-            Op op = getOpLocked(code, uid, resolvedPackageName, isPrivileged, true);
+            Op op = getOpLocked(code, uid, resolvedPackageName, featureId, isPrivileged, true);
             if (op == null) {
                 return;
             }
@@ -2909,22 +3248,24 @@
      *
      * @param uid The uid the package belongs to
      * @param packageName The package the might belong to the uid
+     * @param featureId The feature in the package or {@code null} if no need to verify
      *
      * @return {@code true} iff the package is privileged
      */
-    private boolean verifyAndGetIsPrivileged(int uid, String packageName) {
+    private boolean verifyAndGetIsPrivileged(int uid, String packageName,
+            @Nullable String featureId) {
         if (uid == Process.ROOT_UID) {
             // For backwards compatibility, don't check package name for root UID.
             return false;
         }
 
-        // Do not check if uid/packageName is already known
+        // Do not check if uid/packageName/featureId is already known
         synchronized (this) {
             UidState uidState = mUidStates.get(uid);
             if (uidState != null && uidState.pkgOps != null) {
                 Ops ops = uidState.pkgOps.get(packageName);
 
-                if (ops != null) {
+                if (ops != null && (featureId == null || ops.knownFeatureIds.contains(featureId))) {
                     return ops.isPrivileged;
                 }
             }
@@ -2934,19 +3275,31 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             int pkgUid;
+            AndroidPackage pkg = LocalServices.getService(PackageManagerInternal.class).getPackage(
+                    packageName);
+            boolean isFeatureIdValid = false;
 
-            ApplicationInfo appInfo = LocalServices.getService(PackageManagerInternal.class)
-                    .getApplicationInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
-                                    | PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                    | PackageManager.MATCH_INSTANT,
-                            Process.SYSTEM_UID, UserHandle.getUserId(uid));
-            if (appInfo != null) {
-                pkgUid = appInfo.uid;
-                isPrivileged = (appInfo.privateFlags
-                        & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+            if (pkg != null) {
+                if (featureId == null) {
+                    isFeatureIdValid = true;
+                } else {
+                    if (pkg.getFeatures() != null) {
+                        int numFeatures = pkg.getFeatures().size();
+                        for (int i = 0; i < numFeatures; i++) {
+                            if (pkg.getFeatures().get(i).id.equals(featureId)) {
+                                isFeatureIdValid = true;
+                            }
+                        }
+                    }
+                }
+
+                pkgUid = UserHandle.getUid(
+                        UserHandle.getUserId(uid), UserHandle.getAppId(pkg.getUid()));
+                isPrivileged = pkg.isPrivileged();
             } else {
+                // Allow any feature id for resolvable uids
+                isFeatureIdValid = true;
+
                 pkgUid = resolveUid(packageName);
                 if (pkgUid >= 0) {
                     isPrivileged = false;
@@ -2956,6 +3309,12 @@
                 throw new SecurityException("Specified package " + packageName + " under uid " + uid
                         + " but it is really " + pkgUid);
             }
+
+            if (!isFeatureIdValid) {
+                // TODO moltmann: Switch from logging to enforcement
+                Slog.e(TAG, "featureId " + featureId + " not declared in manifest of "
+                        + packageName);
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -2968,12 +3327,14 @@
      *
      * @param uid The uid the package belongs to
      * @param packageName The name of the package
+     * @param featureId The feature in the package
      * @param isPrivileged If the package is privilidged (ignored if {@code edit} is false)
      * @param edit If an ops does not exist, create the ops?
 
      * @return
      */
-    private Ops getOpsRawLocked(int uid, String packageName, boolean isPrivileged, boolean edit) {
+    private Ops getOpsRawLocked(int uid, String packageName, @Nullable String featureId,
+            boolean isPrivileged, boolean edit) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
             return null;
@@ -2994,6 +3355,9 @@
             ops = new Ops(packageName, uidState, isPrivileged);
             uidState.pkgOps.put(packageName, ops);
         }
+        if (edit && featureId != null) {
+            ops.knownFeatureIds.add(featureId);
+        }
         return ops;
     }
 
@@ -3004,13 +3368,14 @@
      *
      * @param uid The uid the of the package
      * @param packageName The package name for which to get the state for
+     * @param featureId The feature in the package
      * @param edit Iff {@code true} create the {@link Ops} object if not yet created
      * @param isPrivileged Whether the package is privileged or not
      *
      * @return The {@link Ops state} of all ops for the package
      */
     private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName,
-            boolean edit, boolean isPrivileged) {
+            @Nullable String featureId, boolean edit, boolean isPrivileged) {
         UidState uidState = getUidStateLocked(uid, edit);
         if (uidState == null) {
             return null;
@@ -3031,6 +3396,11 @@
             ops = new Ops(packageName, uidState, isPrivileged);
             uidState.pkgOps.put(packageName, ops);
         }
+
+        if (edit && featureId != null) {
+            ops.knownFeatureIds.add(featureId);
+        }
+
         return ops;
     }
 
@@ -3056,6 +3426,7 @@
      * @param code The code of the op
      * @param uid The uid the of the package
      * @param packageName The package name for which to get the state for
+     * @param featureId The feature in the package
      * @param isPrivileged Whether the package is privileged or not (only used if {@code edit
      *                     == true})
      * @param edit Iff {@code true} create the {@link Op} object if not yet created
@@ -3063,21 +3434,21 @@
      * @return The {@link Op state} of the op
      */
     private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName,
-            boolean isPrivileged, boolean edit) {
-        Ops ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
+            @Nullable String featureId, boolean isPrivileged, boolean edit) {
+        Ops ops = getOpsRawNoVerifyLocked(uid, packageName, featureId, edit, isPrivileged);
         if (ops == null) {
             return null;
         }
-        return getOpLocked(ops, code, edit);
+        return getOpLocked(ops, code, uid, edit);
     }
 
-    private Op getOpLocked(Ops ops, int code, boolean edit) {
+    private Op getOpLocked(Ops ops, int code, int uid, boolean edit) {
         Op op = ops.get(code);
         if (op == null) {
             if (!edit) {
                 return null;
             }
-            op = new Op(ops.uidState, ops.packageName, code, ops.uidState.uid);
+            op = new Op(ops.uidState, ops.packageName, code, uid);
             ops.put(code, op);
         }
         if (edit) {
@@ -3095,7 +3466,7 @@
     }
 
     private boolean isOpRestrictedLocked(int uid, int code, String packageName,
-            boolean isPrivileged) {
+            @Nullable String featureId, boolean isPrivileged) {
         int userHandle = UserHandle.getUserId(uid);
         final int restrictionSetCount = mOpUserRestrictions.size();
 
@@ -3107,7 +3478,7 @@
                 if (AppOpsManager.opAllowSystemBypassRestriction(code)) {
                     // If we are the system, bypass user restrictions for certain codes
                     synchronized (this) {
-                        Ops ops = getOpsRawLocked(uid, packageName, isPrivileged,
+                        Ops ops = getOpsRawLocked(uid, packageName, featureId, isPrivileged,
                                 true /* edit */);
                         if ((ops != null) && ops.isPrivileged) {
                             return false;
@@ -3484,7 +3855,7 @@
                         out.startTag(null, "uid");
                         out.attribute(null, "n", Integer.toString(pkg.getUid()));
                         synchronized (this) {
-                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(),
+                            Ops ops = getOpsRawLocked(pkg.getUid(), pkg.getPackageName(), null,
                                     false /* isPrivileged */, false /* edit */);
                             // Should always be present as the list of PackageOps is generated
                             // from Ops.
@@ -4050,7 +4421,8 @@
 
                     if (shell.packageName != null) {
                         shell.mInterface.startOperation(shell.mToken, shell.op, shell.packageUid,
-                                shell.packageName, shell.featureId, true);
+                                shell.packageName, shell.featureId, true, true,
+                                "appops start shell command");
                     } else {
                         return -1;
                     }
@@ -4089,16 +4461,24 @@
         pw.println("    Limit output to data associated with the given app op mode.");
         pw.println("  --package [PACKAGE]");
         pw.println("    Limit output to data associated with the given package name.");
+        pw.println("  --featureId [featureId]");
+        pw.println("    Limit output to data associated with the given feature id.");
         pw.println("  --watchers");
         pw.println("    Only output the watcher sections.");
         pw.println("  --history");
         pw.println("    Output the historical data.");
     }
 
-    private void dumpStatesLocked(@NonNull PrintWriter pw, long nowElapsed, @NonNull Op op,
-            long now, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
+    private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterFeatureId,
+            @HistoricalOpsRequestFilter int filter, long nowElapsed, @NonNull Op op, long now,
+            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix) {
         final int numFeatures = op.mFeatures.size();
         for (int i = 0; i < numFeatures; i++) {
+            if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(op.mFeatures.keyAt(i),
+                    filterFeatureId)) {
+                continue;
+            }
+
             pw.print(prefix + op.mFeatures.keyAt(i) + "=[\n");
             dumpStatesLocked(pw, nowElapsed, op, op.mFeatures.keyAt(i), now, sdf, date,
                     prefix + "  ");
@@ -4188,18 +4568,18 @@
 
         final FeatureOp featureOp = op.mFeatures.get(featureId);
         if (featureOp.isRunning()) {
-            long earliestStartTime = Long.MAX_VALUE;
+            long earliestElapsedTime = Long.MAX_VALUE;
             long maxNumStarts = 0;
             int numInProgressEvents = featureOp.mInProgressEvents.size();
             for (int i = 0; i < numInProgressEvents; i++) {
                 InProgressStartOpEvent event = featureOp.mInProgressEvents.valueAt(i);
 
-                earliestStartTime = Math.min(earliestStartTime, event.startTime);
+                earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
                 maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts);
             }
 
             pw.print(prefix + "Running start at: ");
-            TimeUtils.formatDuration(nowElapsed - earliestStartTime, pw);
+            TimeUtils.formatDuration(nowElapsed - earliestElapsedTime, pw);
             pw.println();
 
             if (maxNumStarts > 1) {
@@ -4215,10 +4595,12 @@
 
         int dumpOp = OP_NONE;
         String dumpPackage = null;
+        String dumpFeatureId = null;
         int dumpUid = Process.INVALID_UID;
         int dumpMode = -1;
         boolean dumpWatchers = false;
         boolean dumpHistory = false;
+        @HistoricalOpsRequestFilter int dumpFilter = 0;
 
         if (args != null) {
             for (int i=0; i<args.length; i++) {
@@ -4235,6 +4617,7 @@
                         return;
                     }
                     dumpOp = Shell.strOpToOp(args[i], pw);
+                    dumpFilter |= FILTER_BY_OP_NAMES;
                     if (dumpOp < 0) {
                         return;
                     }
@@ -4245,6 +4628,7 @@
                         return;
                     }
                     dumpPackage = args[i];
+                    dumpFilter |= FILTER_BY_PACKAGE_NAME;
                     try {
                         dumpUid = AppGlobals.getPackageManager().getPackageUid(dumpPackage,
                                 PackageManager.MATCH_KNOWN_PACKAGES | PackageManager.MATCH_INSTANT,
@@ -4256,6 +4640,15 @@
                         return;
                     }
                     dumpUid = UserHandle.getAppId(dumpUid);
+                    dumpFilter |= FILTER_BY_UID;
+                } else if ("--featureId".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("No argument for --featureId option");
+                        return;
+                    }
+                    dumpFeatureId = args[i];
+                    dumpFilter |= FILTER_BY_FEATURE_ID;
                 } else if ("--mode".equals(arg)) {
                     i++;
                     if (i >= args.length) {
@@ -4292,8 +4685,8 @@
             final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
             final Date date = new Date();
             boolean needSep = false;
-            if (dumpOp < 0 && dumpMode < 0 && dumpPackage == null && mProfileOwners != null
-                    && !dumpWatchers && !dumpHistory) {
+            if (dumpFilter == 0 && dumpMode < 0 && mProfileOwners != null && !dumpWatchers
+                    && !dumpHistory) {
                 pw.println("  Profile owners:");
                 for (int poi = 0; poi < mProfileOwners.size(); poi++) {
                     pw.print("    User #");
@@ -4471,7 +4864,7 @@
                 if (dumpOp >= 0 || dumpPackage != null || dumpMode >= 0) {
                     boolean hasOp = dumpOp < 0 || (uidState.opModes != null
                             && uidState.opModes.indexOfKey(dumpOp) >= 0);
-                    boolean hasPackage = dumpPackage == null;
+                    boolean hasPackage = dumpPackage == null || dumpUid == mUidStates.keyAt(i);
                     boolean hasMode = dumpMode < 0;
                     if (!hasMode && opModes != null) {
                         for (int opi = 0; !hasMode && opi < opModes.size(); opi++) {
@@ -4596,7 +4989,8 @@
                             pw.print("="); pw.print(AppOpsManager.modeToName(mode));
                         }
                         pw.println("): ");
-                        dumpStatesLocked(pw, nowElapsed, op, now, sdf, date, "        ");
+                        dumpStatesLocked(pw, dumpFeatureId, dumpFilter, nowElapsed, op, now, sdf,
+                                date, "        ");
                     }
                 }
             }
@@ -4695,15 +5089,16 @@
 
         // Must not hold the appops lock
         if (dumpHistory && !dumpWatchers) {
-            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpOp);
+            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp,
+                    dumpFilter);
         }
     }
 
     @Override
     public void setUserRestrictions(Bundle restrictions, IBinder token, int userHandle) {
         checkSystemUid("setUserRestrictions");
-        Preconditions.checkNotNull(restrictions);
-        Preconditions.checkNotNull(token);
+        Objects.requireNonNull(restrictions);
+        Objects.requireNonNull(token);
         for (int i = 0; i < AppOpsManager._NUM_OP; i++) {
             String restriction = AppOpsManager.opToRestriction(i);
             if (restriction != null) {
@@ -4730,7 +5125,7 @@
             }
         }
         verifyIncomingOp(code);
-        Preconditions.checkNotNull(token);
+        Objects.requireNonNull(token);
         setUserRestrictionNoCheck(code, restricted, token, userHandle, exceptionPackages);
     }
 
@@ -4801,7 +5196,7 @@
         }
         // TODO moltmann: Allow to check for feature op activeness
         synchronized (AppOpsService.this) {
-            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, false, false);
+            Ops pkgOps = getOpsRawLocked(uid, resolvedPackageName, null, false, false);
             if (pkgOps == null) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 2175ca0..cd450d4 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -15,12 +15,19 @@
  */
 package com.android.server.appop;
 
+import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID;
+import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.app.AppOpsManager.HistoricalFeatureOps;
 import android.app.AppOpsManager.HistoricalMode;
 import android.app.AppOpsManager.HistoricalOp;
 import android.app.AppOpsManager.HistoricalOps;
+import android.app.AppOpsManager.HistoricalOpsRequestFilter;
 import android.app.AppOpsManager.HistoricalPackageOps;
 import android.app.AppOpsManager.HistoricalUidOps;
 import android.app.AppOpsManager.OpFlags;
@@ -72,6 +79,7 @@
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
@@ -273,8 +281,9 @@
                 + "=" + setting + " resetting!");
     }
 
-    void dump(String prefix, PrintWriter pw, int filterUid,
-              String filterPackage, int filterOp) {
+    void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage,
+            @Nullable String filterFeatureId, int filterOp,
+            @HistoricalOpsRequestFilter int filter) {
         if (!isApiEnabled()) {
             return;
         }
@@ -289,7 +298,7 @@
                 pw.println(AppOpsManager.historicalModeToString(mMode));
 
                 final StringDumpVisitor visitor = new StringDumpVisitor(prefix + "  ",
-                        pw, filterUid, filterPackage, filterOp);
+                        pw, filterUid, filterPackage, filterFeatureId, filterOp, filter);
                 final long nowMillis = System.currentTimeMillis();
 
                 // Dump in memory state first
@@ -329,7 +338,8 @@
     }
 
     void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
+            @Nullable String featureId, @Nullable String[] opNames,
+            @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis,
             @OpFlags int flags, @NonNull RemoteCallback callback) {
         if (!isApiEnabled()) {
             callback.sendResult(new Bundle());
@@ -344,8 +354,8 @@
                     return;
                 }
                 final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis);
-                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
-                        beginTimeMillis, endTimeMillis, flags);
+                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+                        opNames, filter, beginTimeMillis, endTimeMillis, flags);
                 final Bundle payload = new Bundle();
                 payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result);
                 callback.sendResult(payload);
@@ -353,9 +363,10 @@
         }
     }
 
-    void getHistoricalOps(int uid, @NonNull String packageName,
-            @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis,
-            @OpFlags int flags, @NonNull RemoteCallback callback) {
+    void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String featureId,
+            @Nullable String[] opNames, @HistoricalOpsRequestFilter int filter,
+            long beginTimeMillis, long endTimeMillis, @OpFlags int flags,
+            @NonNull RemoteCallback callback) {
         if (!isApiEnabled()) {
             callback.sendResult(new Bundle());
             return;
@@ -390,8 +401,8 @@
                         || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) {
                     // Some of the current batch falls into the query, so extract that.
                     final HistoricalOps currentOpsCopy = new HistoricalOps(currentOps);
-                    currentOpsCopy.filter(uid, packageName, opNames, inMemoryAdjBeginTimeMillis,
-                            inMemoryAdjEndTimeMillis);
+                    currentOpsCopy.filter(uid, packageName, featureId, opNames, filter,
+                            inMemoryAdjBeginTimeMillis, inMemoryAdjEndTimeMillis);
                     result.merge(currentOpsCopy);
                 }
                 pendingWrites = new ArrayList<>(mPendingWrites);
@@ -410,8 +421,8 @@
                         - onDiskAndInMemoryOffsetMillis, 0);
                 final long onDiskAdjEndTimeMillis = Math.max(inMemoryAdjEndTimeMillis
                         - onDiskAndInMemoryOffsetMillis, 0);
-                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames,
-                        onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
+                mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, featureId,
+                        opNames, filter, onDiskAdjBeginTimeMillis, onDiskAdjEndTimeMillis, flags);
             }
 
             // Rebase the result time to be since epoch.
@@ -425,43 +436,47 @@
     }
 
     void incrementOpAccessedCount(int op, int uid, @NonNull String packageName,
-            @UidState int uidState, @OpFlags int flags) {
+            @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
                     Slog.e(LOG_TAG, "Interaction before persistence initialized");
                     return;
                 }
-                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
-                        .increaseAccessCount(op, uid, packageName, uidState, flags, 1);
+                getUpdatedPendingHistoricalOpsMLocked(
+                        System.currentTimeMillis()).increaseAccessCount(op, uid, packageName,
+                        featureId, uidState, flags, 1);
             }
         }
     }
 
     void incrementOpRejected(int op, int uid, @NonNull String packageName,
-            @UidState int uidState, @OpFlags int flags) {
+            @Nullable String featureId, @UidState int uidState, @OpFlags int flags) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
                     Slog.e(LOG_TAG, "Interaction before persistence initialized");
                     return;
                 }
-                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
-                        .increaseRejectCount(op, uid, packageName, uidState, flags, 1);
+                getUpdatedPendingHistoricalOpsMLocked(
+                        System.currentTimeMillis()).increaseRejectCount(op, uid, packageName,
+                        featureId, uidState, flags, 1);
             }
         }
     }
 
     void increaseOpAccessDuration(int op, int uid, @NonNull String packageName,
-            @UidState int uidState, @OpFlags int flags, long increment) {
+            @Nullable String featureId, @UidState int uidState, @OpFlags int flags,
+            long increment) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
                     Slog.e(LOG_TAG, "Interaction before persistence initialized");
                     return;
                 }
-                getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis())
-                        .increaseAccessDuration(op, uid, packageName, uidState, flags, increment);
+                getUpdatedPendingHistoricalOpsMLocked(
+                        System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
+                        featureId, uidState, flags, increment);
             }
         }
     }
@@ -713,6 +728,7 @@
         private static final String TAG_OPS = "ops";
         private static final String TAG_UID = "uid";
         private static final String TAG_PACKAGE = "pkg";
+        private static final String TAG_FEATURE = "ftr";
         private static final String TAG_OP = "op";
         private static final String TAG_STATE = "st";
 
@@ -791,8 +807,8 @@
 
         @Nullable List<HistoricalOps> readHistoryRawDLocked() {
             return collectHistoricalOpsBaseDLocked(Process.INVALID_UID /*filterUid*/,
-                    null /*filterPackageName*/, null /*filterOpNames*/,
-                    0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
+                    null /*filterPackageName*/, null /*filterFeatureId*/, null /*filterOpNames*/,
+                    0 /*filter*/, 0 /*filterBeginTimeMills*/, Long.MAX_VALUE /*filterEndTimeMills*/,
                     AppOpsManager.OP_FLAGS_ALL);
         }
 
@@ -846,11 +862,12 @@
         }
 
         private void collectHistoricalOpsDLocked(@NonNull HistoricalOps currentOps,
-                int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
+                int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeingMillis, long filterEndMillis, @OpFlags int filterFlags) {
             final List<HistoricalOps> readOps = collectHistoricalOpsBaseDLocked(filterUid,
-                    filterPackageName, filterOpNames, filterBeingMillis, filterEndMillis,
-                    filterFlags);
+                    filterPackageName, filterFeatureId, filterOpNames, filter, filterBeingMillis,
+                    filterEndMillis, filterFlags);
             if (readOps != null) {
                 final int readCount = readOps.size();
                 for (int i = 0; i < readCount; i++) {
@@ -861,7 +878,8 @@
         }
 
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked(
-                int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames,
+                int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) {
             File baseDir = null;
             try {
@@ -874,9 +892,9 @@
                 final Set<String> historyFiles = getHistoricalFileNames(baseDir);
                 final long[] globalContentOffsetMillis = {0};
                 final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked(
-                        baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
-                        filterEndTimeMillis, filterFlags, globalContentOffsetMillis,
-                        null /*outOps*/, 0 /*depth*/, historyFiles);
+                        baseDir, filterUid, filterPackageName, filterFeatureId, filterOpNames,
+                        filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+                        globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles);
                 if (DEBUG) {
                     filesInvariant.stopTracking(baseDir);
                 }
@@ -890,8 +908,9 @@
         }
 
         private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsRecursiveDLocked(
-                @NonNull File baseDir, int filterUid, @NonNull String filterPackageName,
-                @Nullable String[] filterOpNames, long filterBeginTimeMillis,
+                @NonNull File baseDir, int filterUid, @Nullable String filterPackageName,
+                @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+                @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
                 long filterEndTimeMillis, @OpFlags int filterFlags,
                 @NonNull long[] globalContentOffsetMillis,
                 @Nullable LinkedList<HistoricalOps> outOps, int depth,
@@ -908,17 +927,17 @@
             // Read historical data at this level
             final List<HistoricalOps> readOps = readHistoricalOpsLocked(baseDir,
                     previousIntervalEndMillis, currentIntervalEndMillis, filterUid,
-                    filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis,
-                    filterFlags, globalContentOffsetMillis, depth, historyFiles);
-
+                    filterPackageName, filterFeatureId, filterOpNames, filter,
+                    filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+                    globalContentOffsetMillis, depth, historyFiles);
             // Empty is a special signal to stop diving
             if (readOps != null && readOps.isEmpty()) {
                 return outOps;
             }
 
             // Collect older historical data from subsequent levels
-            outOps = collectHistoricalOpsRecursiveDLocked(
-                    baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
+            outOps = collectHistoricalOpsRecursiveDLocked(baseDir, filterUid, filterPackageName,
+                    filterFeatureId, filterOpNames, filter, filterBeginTimeMillis,
                     filterEndTimeMillis, filterFlags, globalContentOffsetMillis, outOps, depth + 1,
                     historyFiles);
 
@@ -987,10 +1006,10 @@
             final List<HistoricalOps> existingOps = readHistoricalOpsLocked(oldBaseDir,
                     previousIntervalEndMillis, currentIntervalEndMillis,
                     Process.INVALID_UID /*filterUid*/, null /*filterPackageName*/,
-                    null /*filterOpNames*/, Long.MIN_VALUE /*filterBeginTimeMillis*/,
-                    Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL,
-                    null, depth, null /*historyFiles*/);
-
+                    null /*filterFeatureId*/, null /*filterOpNames*/, 0 /*filter*/,
+                    Long.MIN_VALUE /*filterBeginTimeMillis*/,
+                    Long.MAX_VALUE /*filterEndTimeMillis*/, AppOpsManager.OP_FLAGS_ALL, null, depth,
+                    null /*historyFiles*/);
             if (DEBUG) {
                 enforceOpsWellFormed(existingOps);
             }
@@ -1100,8 +1119,9 @@
         }
 
         private @Nullable List<HistoricalOps> readHistoricalOpsLocked(File baseDir,
-                long intervalBeginMillis, long intervalEndMillis,
-                int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                long intervalBeginMillis, long intervalEndMillis, int filterUid,
+                @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis, int depth,
                 @NonNull Set<String> historyFiles)
@@ -1127,13 +1147,14 @@
                     return null;
                 }
             }
-            return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterOpNames,
-                    filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+            return readHistoricalOpsLocked(file, filterUid, filterPackageName, filterFeatureId,
+                    filterOpNames, filter, filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
                     cumulativeOverflowMillis);
         }
 
         private @Nullable List<HistoricalOps> readHistoricalOpsLocked(@NonNull File file,
-                int filterUid, @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                int filterUid, @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
@@ -1158,9 +1179,10 @@
                 final int depth = parser.getDepth();
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_OPS.equals(parser.getName())) {
-                        final HistoricalOps ops = readeHistoricalOpsDLocked(parser,
-                                filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis,
-                                filterEndTimeMillis, filterFlags, cumulativeOverflowMillis);
+                        final HistoricalOps ops = readeHistoricalOpsDLocked(parser, filterUid,
+                                filterPackageName, filterFeatureId, filterOpNames, filter,
+                                filterBeginTimeMillis, filterEndTimeMillis, filterFlags,
+                                cumulativeOverflowMillis);
                         if (ops == null) {
                             continue;
                         }
@@ -1193,7 +1215,8 @@
 
         private @Nullable HistoricalOps readeHistoricalOpsDLocked(
                 @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
-                @Nullable String[] filterOpNames, long filterBeginTimeMillis,
+                @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+                @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
                 long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
@@ -1222,7 +1245,8 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_UID.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalUidOpsDLocked(ops, parser,
-                            filterUid, filterPackageName, filterOpNames, filterFlags, filterScale);
+                            filterUid, filterPackageName, filterFeatureId, filterOpNames, filter,
+                            filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1236,11 +1260,12 @@
 
         private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
                 @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
-                @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
             final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME);
-            if (filterUid != Process.INVALID_UID && filterUid != uid) {
+            if ((filter & FILTER_BY_UID) != 0 && filterUid != uid) {
                 XmlUtils.skipCurrentTag(parser);
                 return null;
             }
@@ -1248,8 +1273,8 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_PACKAGE.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readHistoricalPackageOpsDLocked(ops,
-                            uid, parser, filterPackageName, filterOpNames, filterFlags,
-                            filterScale);
+                            uid, parser, filterPackageName, filterFeatureId, filterOpNames, filter,
+                            filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1260,19 +1285,46 @@
 
         private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
                 @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
-                @Nullable String filterPackageName, @Nullable String[] filterOpNames,
+                @Nullable String filterPackageName, @Nullable String filterFeatureId,
+                @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
             final String packageName = XmlUtils.readStringAttribute(parser, ATTR_NAME);
-            if (filterPackageName != null && !filterPackageName.equals(packageName)) {
+            if ((filter & FILTER_BY_PACKAGE_NAME) != 0 && !filterPackageName.equals(packageName)) {
+                XmlUtils.skipCurrentTag(parser);
+                return null;
+            }
+            final int depth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, depth)) {
+                if (TAG_FEATURE.equals(parser.getName())) {
+                    final HistoricalOps returnedOps = readHistoricalFeatureOpsDLocked(ops, uid,
+                            packageName, parser, filterFeatureId, filterOpNames, filter,
+                            filterFlags, filterScale);
+                    if (ops == null) {
+                        ops = returnedOps;
+                    }
+                }
+            }
+            return ops;
+        }
+
+        private @Nullable HistoricalOps readHistoricalFeatureOpsDLocked(@Nullable HistoricalOps ops,
+                int uid, String packageName, @NonNull XmlPullParser parser,
+                @Nullable String filterFeatureId, @Nullable String[] filterOpNames,
+                @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
+                double filterScale)
+                throws IOException, XmlPullParserException {
+            final String featureId = XmlUtils.readStringAttribute(parser, ATTR_NAME);
+            if ((filter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(filterFeatureId,
+                    featureId)) {
                 XmlUtils.skipCurrentTag(parser);
                 return null;
             }
             final int depth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_OP.equals(parser.getName())) {
-                    final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid,
-                            packageName, parser, filterOpNames, filterFlags, filterScale);
+                    final HistoricalOps returnedOps = readHistoricalOpDLocked(ops, uid, packageName,
+                            featureId, parser, filterOpNames, filter, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1282,11 +1334,13 @@
         }
 
         private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
-                int uid, String packageName, @NonNull XmlPullParser parser,
-                @Nullable String[] filterOpNames, @OpFlags int filterFlags, double filterScale)
+                int uid, @NonNull String packageName, @Nullable String featureId,
+                @NonNull XmlPullParser parser, @Nullable String[] filterOpNames,
+                @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
+                double filterScale)
                 throws IOException, XmlPullParserException {
             final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME);
-            if (filterOpNames != null && !ArrayUtils.contains(filterOpNames,
+            if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(filterOpNames,
                     AppOpsManager.opToPublicName(op))) {
                 XmlUtils.skipCurrentTag(parser);
                 return null;
@@ -1295,7 +1349,7 @@
             while (XmlUtils.nextElementWithin(parser, depth)) {
                 if (TAG_STATE.equals(parser.getName())) {
                     final HistoricalOps returnedOps = readStateDLocked(ops, uid,
-                            packageName, op, parser, filterFlags, filterScale);
+                            packageName, featureId, op, parser, filterFlags, filterScale);
                     if (ops == null) {
                         ops = returnedOps;
                     }
@@ -1305,8 +1359,9 @@
         }
 
         private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
-                int uid, String packageName, int op, @NonNull XmlPullParser parser,
-                @OpFlags int filterFlags, double filterScale) throws IOException {
+                int uid, @NonNull String packageName, @Nullable String featureId, int op,
+                @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale)
+                throws IOException {
             final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
             final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags;
             if (flags == 0) {
@@ -1322,7 +1377,8 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseAccessCount(op, uid, packageName, uidState, flags, accessCount);
+                ops.increaseAccessCount(op, uid, packageName, featureId, uidState, flags,
+                        accessCount);
             }
             long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
             if (rejectCount > 0) {
@@ -1333,7 +1389,8 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseRejectCount(op, uid, packageName, uidState, flags, rejectCount);
+                ops.increaseRejectCount(op, uid, packageName, featureId, uidState, flags,
+                        rejectCount);
             }
             long accessDuration =  XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
             if (accessDuration > 0) {
@@ -1344,7 +1401,8 @@
                 if (ops == null) {
                     ops = new HistoricalOps(0, 0);
                 }
-                ops.increaseAccessDuration(op, uid, packageName, uidState, flags, accessDuration);
+                ops.increaseAccessDuration(op, uid, packageName, featureId, uidState, flags,
+                        accessDuration);
             }
             return ops;
         }
@@ -1409,14 +1467,26 @@
                 @NonNull XmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_PACKAGE);
             serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
-            final int opCount = packageOps.getOpCount();
-            for (int i = 0; i < opCount; i++) {
-                final HistoricalOp op = packageOps.getOpAt(i);
-                writeHistoricalOpDLocked(op, serializer);
+            final int numFeatures = packageOps.getFeatureCount();
+            for (int i = 0; i < numFeatures; i++) {
+                final HistoricalFeatureOps op = packageOps.getFeatureOpsAt(i);
+                writeHistoricalFeatureOpsDLocked(op, serializer);
             }
             serializer.endTag(null, TAG_PACKAGE);
         }
 
+        private void writeHistoricalFeatureOpsDLocked(@NonNull HistoricalFeatureOps featureOps,
+                @NonNull XmlSerializer serializer) throws IOException {
+            serializer.startTag(null, TAG_FEATURE);
+            XmlUtils.writeStringAttribute(serializer, ATTR_NAME, featureOps.getFeatureId());
+            final int opCount = featureOps.getOpCount();
+            for (int i = 0; i < opCount; i++) {
+                final HistoricalOp op = featureOps.getOpAt(i);
+                writeHistoricalOpDLocked(op, serializer);
+            }
+            serializer.endTag(null, TAG_FEATURE);
+        }
+
         private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
                 @NonNull XmlSerializer serializer) throws IOException {
             final LongSparseArray keys = op.collectKeys();
@@ -1648,24 +1718,31 @@
         private final @NonNull String mOpsPrefix;
         private final @NonNull String mUidPrefix;
         private final @NonNull String mPackagePrefix;
+        private final @NonNull String mFeaturePrefix;
         private final @NonNull String mEntryPrefix;
         private final @NonNull String mUidStatePrefix;
         private final @NonNull PrintWriter mWriter;
         private final int mFilterUid;
         private final String mFilterPackage;
+        private final String mFilterFeatureId;
         private final int mFilterOp;
+        private final @HistoricalOpsRequestFilter int mFilter;
 
-        StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer,
-                int filterUid, String filterPackage, int filterOp) {
+        StringDumpVisitor(@NonNull String prefix, @NonNull PrintWriter writer, int filterUid,
+                @Nullable String filterPackage, @Nullable String filterFeatureId, int filterOp,
+                @HistoricalOpsRequestFilter int filter) {
             mOpsPrefix = prefix + "  ";
             mUidPrefix = mOpsPrefix + "  ";
             mPackagePrefix = mUidPrefix + "  ";
-            mEntryPrefix = mPackagePrefix + "  ";
+            mFeaturePrefix = mPackagePrefix + "  ";
+            mEntryPrefix = mFeaturePrefix + "  ";
             mUidStatePrefix = mEntryPrefix + "  ";
             mWriter = writer;
             mFilterUid = filterUid;
             mFilterPackage = filterPackage;
+            mFilterFeatureId = filterFeatureId;
             mFilterOp = filterOp;
+            mFilter = filter;
         }
 
         @Override
@@ -1691,7 +1768,7 @@
 
         @Override
         public void visitHistoricalUidOps(HistoricalUidOps ops) {
-            if (mFilterUid != Process.INVALID_UID && mFilterUid != ops.getUid()) {
+            if ((mFilter & FILTER_BY_UID) != 0 && mFilterUid != ops.getUid()) {
                 return;
             }
             mWriter.println();
@@ -1703,7 +1780,8 @@
 
         @Override
         public void visitHistoricalPackageOps(HistoricalPackageOps ops) {
-            if (mFilterPackage != null && !mFilterPackage.equals(ops.getPackageName())) {
+            if ((mFilter & FILTER_BY_PACKAGE_NAME) != 0 && !mFilterPackage.equals(
+                    ops.getPackageName())) {
                 return;
             }
             mWriter.print(mPackagePrefix);
@@ -1713,8 +1791,20 @@
         }
 
         @Override
+        public void visitHistoricalFeatureOps(HistoricalFeatureOps ops) {
+            if ((mFilter & FILTER_BY_FEATURE_ID) != 0 && !Objects.equals(mFilterPackage,
+                    ops.getFeatureId())) {
+                return;
+            }
+            mWriter.print(mFeaturePrefix);
+            mWriter.print("Feature ");
+            mWriter.print(ops.getFeatureId());
+            mWriter.println(":");
+        }
+
+        @Override
         public void visitHistoricalOp(HistoricalOp ops) {
-            if (mFilterOp != AppOpsManager.OP_NONE && mFilterOp != ops.getOpCode()) {
+            if ((mFilter & FILTER_BY_OP_NAMES) != 0  && mFilterOp != ops.getOpCode()) {
                 return;
             }
             mWriter.print(mEntryPrefix);
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index fc67e24..5ac5b29 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -67,6 +67,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * An attention service implementation that runs in System Server process.
@@ -120,7 +121,7 @@
     AttentionManagerService(Context context, PowerManager powerManager, Object lock,
             AttentionHandler handler) {
         super(context);
-        mContext = Preconditions.checkNotNull(context);
+        mContext = Objects.requireNonNull(context);
         mPowerManager = powerManager;
         mLock = lock;
         mAttentionHandler = handler;
@@ -200,7 +201,7 @@
      */
     @VisibleForTesting
     boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
-        Preconditions.checkNotNull(callbackInternal);
+        Objects.requireNonNull(callbackInternal);
 
         if (!isAttentionServiceSupported()) {
             Slog.w(LOG_TAG, "Trying to call checkAttention() on an unsupported device.");
@@ -543,9 +544,9 @@
         UserState(int userId, Context context, Object lock, Handler handler,
                 ComponentName componentName) {
             mUserId = userId;
-            mContext = Preconditions.checkNotNull(context);
-            mLock = Preconditions.checkNotNull(lock);
-            mComponentName = Preconditions.checkNotNull(componentName);
+            mContext = Objects.requireNonNull(context);
+            mLock = Objects.requireNonNull(lock);
+            mComponentName = Objects.requireNonNull(componentName);
             mAttentionHandler = handler;
         }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 114aac9..bc4dc8f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2196,7 +2196,7 @@
     public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags,
                                             String callingPackage) {
         enforceModifyAudioRoutingPermission();
-        Preconditions.checkNotNull(attr, "attr must not be null");
+        Objects.requireNonNull(attr, "attr must not be null");
         // @todo not hold the caller context, post message
         int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
         final int device = getDeviceForStream(stream);
@@ -2231,7 +2231,7 @@
     /** @see AudioManager#getVolumeIndexForAttributes(attr) */
     public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         enforceModifyAudioRoutingPermission();
-        Preconditions.checkNotNull(attr, "attr must not be null");
+        Objects.requireNonNull(attr, "attr must not be null");
         int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
         final int device = getDeviceForStream(stream);
 
@@ -2241,14 +2241,14 @@
     /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
     public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         enforceModifyAudioRoutingPermission();
-        Preconditions.checkNotNull(attr, "attr must not be null");
+        Objects.requireNonNull(attr, "attr must not be null");
         return AudioSystem.getMaxVolumeIndexForAttributes(attr);
     }
 
     /** @see AudioManager#getMinVolumeIndexForAttributes(attr) */
     public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
         enforceModifyAudioRoutingPermission();
-        Preconditions.checkNotNull(attr, "attr must not be null");
+        Objects.requireNonNull(attr, "attr must not be null");
         return AudioSystem.getMinVolumeIndexForAttributes(attr);
     }
 
@@ -2510,7 +2510,7 @@
 
 
     private int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) {
-        Preconditions.checkNotNull(attributes, "attributes must not be null");
+        Objects.requireNonNull(attributes, "attributes must not be null");
         int volumeGroupId = getVolumeGroupIdForAttributesInt(attributes);
         if (volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
             return volumeGroupId;
@@ -2521,7 +2521,7 @@
     }
 
     private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) {
-        Preconditions.checkNotNull(attributes, "attributes must not be null");
+        Objects.requireNonNull(attributes, "attributes must not be null");
         for (final AudioProductStrategy productStrategy :
                 AudioProductStrategy.getAudioProductStrategies()) {
             int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index ff0b015..28f67fe 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -34,6 +34,7 @@
 
 import java.io.PrintWriter;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * A utility to map from an ambient brightness to a display's "backlight" brightness based on the
@@ -702,7 +703,7 @@
                     "Nits and backlight arrays must not be empty!");
             Preconditions.checkArgument(nits.length == backlight.length,
                     "Nits and backlight arrays must be the same length!");
-            Preconditions.checkNotNull(config);
+            Objects.requireNonNull(config);
             Preconditions.checkArrayElementsInRange(nits, 0, Float.MAX_VALUE, "nits");
             Preconditions.checkArrayElementsInRange(backlight,
                     PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON, "backlight");
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 10386e7..e39d6d5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -77,7 +77,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.IntArray;
 import android.util.Pair;
@@ -1346,6 +1345,35 @@
         return SurfaceControl.getDisplayedContentSample(token, maxFrames, timestamp);
     }
 
+    void resetBrightnessConfiguration() {
+        setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
+                mContext.getPackageName());
+    }
+
+    void setAutoBrightnessLoggingEnabled(boolean enabled) {
+        if (mDisplayPowerController != null) {
+            synchronized (mSyncRoot) {
+                mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+            }
+        }
+    }
+
+    void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+        if (mDisplayPowerController != null) {
+            synchronized (mSyncRoot) {
+                mDisplayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
+            }
+        }
+    }
+
+    void setAmbientColorTemperatureOverride(float cct) {
+        if (mDisplayPowerController != null) {
+            synchronized (mSyncRoot) {
+                mDisplayPowerController.setAmbientColorTemperatureOverride(cct);
+            }
+        }
+    }
+
     private void onDesiredDisplayModeSpecsChangedInternal() {
         boolean changed = false;
         synchronized (mSyncRoot) {
@@ -2249,13 +2277,8 @@
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
                 FileDescriptor err, String[] args, ShellCallback callback,
                 ResultReceiver resultReceiver) {
-            final long token = Binder.clearCallingIdentity();
-            try {
-                DisplayManagerShellCommand command = new DisplayManagerShellCommand(this);
-                command.exec(this, in, out, err, args, callback, resultReceiver);
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
+            new DisplayManagerShellCommand(DisplayManagerService.this).exec(this, in, out, err,
+                    args, callback, resultReceiver);
         }
 
         @Override // Binder call
@@ -2278,40 +2301,6 @@
             }
         }
 
-        void setBrightness(int brightness) {
-            Settings.System.putIntForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);
-        }
-
-        void resetBrightnessConfiguration() {
-            setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
-                    mContext.getPackageName());
-        }
-
-        void setAutoBrightnessLoggingEnabled(boolean enabled) {
-            if (mDisplayPowerController != null) {
-                synchronized (mSyncRoot) {
-                    mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
-                }
-            }
-        }
-
-        void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
-            if (mDisplayPowerController != null) {
-                synchronized (mSyncRoot) {
-                    mDisplayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
-                }
-            }
-        }
-
-        void setAmbientColorTemperatureOverride(float cct) {
-            if (mDisplayPowerController != null) {
-                synchronized (mSyncRoot) {
-                    mDisplayPowerController.setAmbientColorTemperatureOverride(cct);
-                }
-            }
-        }
-
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 04d28ea..e87ad41 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -16,17 +16,22 @@
 
 package com.android.server.display;
 
+import android.Manifest;
+import android.content.Context;
 import android.content.Intent;
+import android.os.Binder;
 import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.provider.Settings;
 
 import java.io.PrintWriter;
 
 class DisplayManagerShellCommand extends ShellCommand {
     private static final String TAG = "DisplayManagerShellCommand";
 
-    private final DisplayManagerService.BinderService mService;
+    private final DisplayManagerService mService;
 
-    DisplayManagerShellCommand(DisplayManagerService.BinderService service) {
+    DisplayManagerShellCommand(DisplayManagerService service) {
         mService = service;
     }
 
@@ -96,7 +101,19 @@
             getErrPrintWriter().println("Error: brightness should be a number between 0 and 1");
             return 1;
         }
-        mService.setBrightness((int) (brightness * 255));
+
+        final Context context = mService.getContext();
+        context.enforceCallingOrSelfPermission(
+                Manifest.permission.CONTROL_DISPLAY_BRIGHTNESS,
+                "Permission required to set the display's brightness");
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Settings.System.putIntForUser(context.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS, (int) (brightness * 255),
+                    UserHandle.USER_CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index eebc738..cf94d46 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -37,6 +37,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.LocalServices;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
@@ -641,9 +642,7 @@
 
         @Override
         public void setRequestedColorModeLocked(int colorMode) {
-            if (requestColorModeLocked(colorMode)) {
-                updateDeviceInfoLocked();
-            }
+            requestColorModeLocked(colorMode);
         }
 
         @Override
@@ -671,12 +670,22 @@
             if (mDisplayModeSpecsInvalid || !displayModeSpecs.equals(mDisplayModeSpecs)) {
                 mDisplayModeSpecsInvalid = false;
                 mDisplayModeSpecs.copyFrom(displayModeSpecs);
-                final IBinder token = getDisplayTokenLocked();
-                SurfaceControl.setDesiredDisplayConfigSpecs(token,
+                getHandler().sendMessage(PooledLambda.obtainMessage(
+                        LocalDisplayDevice::setDesiredDisplayModeSpecsAsync, this,
+                        getDisplayTokenLocked(),
                         new SurfaceControl.DesiredDisplayConfigSpecs(defaultPhysIndex,
                                 mDisplayModeSpecs.refreshRateRange.min,
-                                mDisplayModeSpecs.refreshRateRange.max));
-                int activePhysIndex = SurfaceControl.getActiveConfig(token);
+                                mDisplayModeSpecs.refreshRateRange.max)));
+            }
+        }
+
+        private void setDesiredDisplayModeSpecsAsync(IBinder displayToken,
+                SurfaceControl.DesiredDisplayConfigSpecs configSpecs) {
+            // Do not lock when calling these SurfaceControl methods because they are sync
+            // operations that may block for a while when setting display power mode.
+            SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
+            final int activePhysIndex = SurfaceControl.getActiveConfig(displayToken);
+            synchronized (getSyncRoot()) {
                 if (updateActiveModeLocked(activePhysIndex)) {
                     updateDeviceInfoLocked();
                 }
@@ -708,19 +717,30 @@
             return true;
         }
 
-        public boolean requestColorModeLocked(int colorMode) {
+        public void requestColorModeLocked(int colorMode) {
             if (mActiveColorMode == colorMode) {
-                return false;
+                return;
             }
             if (!mSupportedColorModes.contains(colorMode)) {
                 Slog.w(TAG, "Unable to find color mode " + colorMode
                         + ", ignoring request.");
-                return false;
+                return;
             }
-            SurfaceControl.setActiveColorMode(getDisplayTokenLocked(), colorMode);
+
             mActiveColorMode = colorMode;
             mActiveColorModeInvalid = false;
-            return true;
+            getHandler().sendMessage(PooledLambda.obtainMessage(
+                    LocalDisplayDevice::requestColorModeAsync, this,
+                    getDisplayTokenLocked(), colorMode));
+        }
+
+        private void requestColorModeAsync(IBinder displayToken, int colorMode) {
+            // Do not lock when calling this SurfaceControl method because it is a sync operation
+            // that may block for a while when setting display power mode.
+            SurfaceControl.setActiveColorMode(displayToken, colorMode);
+            synchronized (getSyncRoot()) {
+                updateDeviceInfoLocked();
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java b/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
index 1707a62..75d00ff 100644
--- a/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
+++ b/services/core/java/com/android/server/display/whitebalance/AmbientSensor.java
@@ -29,6 +29,7 @@
 import com.android.server.display.utils.History;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * The DisplayWhiteBalanceController uses the AmbientSensor to detect changes in the ambient
@@ -139,8 +140,8 @@
 
 
     private static void validateArguments(Handler handler, SensorManager sensorManager, int rate) {
-        Preconditions.checkNotNull(handler, "handler cannot be null");
-        Preconditions.checkNotNull(sensorManager, "sensorManager cannot be null");
+        Objects.requireNonNull(handler, "handler cannot be null");
+        Objects.requireNonNull(sensorManager, "sensorManager cannot be null");
         if (rate <= 0) {
             throw new IllegalArgumentException("rate must be positive");
         }
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index 88a7077..d64fcbc 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -28,6 +28,7 @@
 import com.android.server.display.utils.History;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * The DisplayWhiteBalanceController drives display white-balance (automatically correcting the
@@ -475,13 +476,13 @@
             AmbientSensor.AmbientColorTemperatureSensor colorTemperatureSensor,
             AmbientFilter colorTemperatureFilter,
             DisplayWhiteBalanceThrottler throttler) {
-        Preconditions.checkNotNull(brightnessSensor, "brightnessSensor must not be null");
-        Preconditions.checkNotNull(brightnessFilter, "brightnessFilter must not be null");
-        Preconditions.checkNotNull(colorTemperatureSensor,
+        Objects.requireNonNull(brightnessSensor, "brightnessSensor must not be null");
+        Objects.requireNonNull(brightnessFilter, "brightnessFilter must not be null");
+        Objects.requireNonNull(colorTemperatureSensor,
                 "colorTemperatureSensor must not be null");
-        Preconditions.checkNotNull(colorTemperatureFilter,
+        Objects.requireNonNull(colorTemperatureFilter,
                 "colorTemperatureFilter must not be null");
-        Preconditions.checkNotNull(throttler, "throttler cannot be null");
+        Objects.requireNonNull(throttler, "throttler cannot be null");
     }
 
     private boolean enable() {
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
index 6e78894..0efb749 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java
@@ -30,6 +30,7 @@
 import com.android.server.display.whitebalance.DisplayWhiteBalanceController.Callbacks;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 
 /**
  * The DisplayWhiteBalanceSettings holds the state of all the settings related to
@@ -144,8 +145,8 @@
     }
 
     private void validateArguments(Context context, Handler handler) {
-        Preconditions.checkNotNull(context, "context must not be null");
-        Preconditions.checkNotNull(handler, "handler must not be null");
+        Objects.requireNonNull(context, "context must not be null");
+        Objects.requireNonNull(handler, "handler must not be null");
     }
 
     private void setEnabled(boolean enabled) {
diff --git a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
index 080e6ce..a9e8719 100755
--- a/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
+++ b/services/core/java/com/android/server/hdmi/DeviceDiscoveryAction.java
@@ -26,6 +26,7 @@
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Feature action that handles device discovery sequences.
@@ -106,7 +107,7 @@
      */
     DeviceDiscoveryAction(HdmiCecLocalDevice source, DeviceDiscoveryCallback callback, int delay) {
         super(source);
-        mCallback = Preconditions.checkNotNull(callback);
+        mCallback = Objects.requireNonNull(callback);
         mDelayPeriod = delay;
     }
 
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 58f6ba2..c58000f 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -50,6 +50,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Base class for {@link SystemService SystemServices} that support multi user.
@@ -373,7 +374,7 @@
                 + durationMs + "ms");
         enforceCallingPermissionForManagement();
 
-        Preconditions.checkNotNull(componentName);
+        Objects.requireNonNull(componentName);
         final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
         if (durationMs > maxDurationMs) {
             throw new IllegalArgumentException(
@@ -735,7 +736,7 @@
      * @throws SecurityException when it's not...
      */
     protected final void assertCalledByPackageOwner(@NonNull String packageName) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         final int uid = Binder.getCallingUid();
         final String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
         if (packages != null) {
@@ -954,6 +955,35 @@
                 onServicePackageRestartedLocked(userId);
             }
 
+            @Override
+            public void onPackageModified(String packageName) {
+                if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
+
+                final int userId = getChangingUserId();
+                final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
+                if (serviceName == null) {
+                    return;
+                }
+
+                final ComponentName serviceComponentName =
+                        ComponentName.unflattenFromString(serviceName);
+                if (serviceComponentName == null
+                        || !serviceComponentName.getPackageName().equals(packageName)) {
+                    return;
+                }
+
+                // The default service package has changed, update the cached if the service
+                // exists but no active component.
+                final S service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    final ComponentName componentName = service.getServiceComponentName();
+                    if (componentName == null) {
+                        if (verbose) Slog.v(mTag, "update cached");
+                        updateCachedServiceLocked(userId);
+                    }
+                }
+            }
+
             private String getActiveServicePackageNameLocked() {
                 final int userId = getChangingUserId();
                 final S service = peekServiceForUserLocked(userId);
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bf73aa3..ae6d63a 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1719,7 +1719,7 @@
     // Binder call
     @Override
     public void setCustomPointerIcon(PointerIcon icon) {
-        Preconditions.checkNotNull(icon);
+        Objects.requireNonNull(icon);
         nativeSetCustomPointerIcon(mPtr, icon);
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index 44c8971..c40e8af 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -19,8 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.ComponentName;
-import android.os.RemoteException;
-import android.util.Log;
 import android.view.autofill.AutofillId;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InputMethodInfo;
@@ -99,12 +97,6 @@
                 @Override
                 public void onCreateInlineSuggestionsRequest(ComponentName componentName,
                         AutofillId autofillId, IInlineSuggestionsRequestCallback cb) {
-                    try {
-                        cb.onInlineSuggestionsUnsupported();
-                    } catch (RemoteException e) {
-                        Log.w("IMManagerInternal", "RemoteException calling"
-                                + " onInlineSuggestionsUnsupported: " + e);
-                    }
                 }
             };
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5865dc4..8b6b614 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1792,10 +1792,18 @@
     @GuardedBy("mMethodMap")
     private void onCreateInlineSuggestionsRequestLocked(ComponentName componentName,
             AutofillId autofillId, IInlineSuggestionsRequestCallback callback) {
-        if (mCurMethod != null) {
-            executeOrSendMessage(mCurMethod,
-                    mCaller.obtainMessageOOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
-                            componentName, autofillId, callback));
+
+        final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
+        try {
+            if (imi != null && imi.isInlineSuggestionsEnabled() && mCurMethod != null) {
+                executeOrSendMessage(mCurMethod,
+                        mCaller.obtainMessageOOOO(MSG_INLINE_SUGGESTIONS_REQUEST, mCurMethod,
+                                componentName, autofillId, callback));
+            } else {
+                callback.onInlineSuggestionsUnsupported();
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "RemoteException calling onCreateInlineSuggestionsRequest(): " + e);
         }
     }
 
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index c13d55a..1f9379c 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -59,7 +59,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.InputChannel;
@@ -198,8 +197,7 @@
                                 //TODO(b/137800469): support multi client IMEs.
                                 cb.onInlineSuggestionsUnsupported();
                             } catch (RemoteException e) {
-                                Log.w("MultiClientIMManager", "RemoteException calling"
-                                        + " onInlineSuggestionsUnsupported: " + e);
+                                Slog.w(TAG, "Failed to call onInlineSuggestionsUnsupported.", e);
                             }
                         }
                     });
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index f3d60f5..b1639a9 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -196,6 +196,15 @@
 
     private void handleIntegrityVerification(Intent intent) {
         int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
+
+        // Fail early if we don't have any rules at all.
+        if (!mIntegrityFileManager.initialized()) {
+            Slog.i(TAG, "Rules not initialized. Skipping integrity check.");
+            mPackageManagerInternal.setIntegrityVerificationResult(
+                    verificationId, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+            return;
+        }
+
         try {
             Slog.i(TAG, "Received integrity verification intent " + intent.toString());
             Slog.i(TAG, "Extras " + intent.getExtras());
@@ -376,6 +385,9 @@
                         String packageName = getPackageNameNormalized(packageAndCert[0]);
                         String cert = packageAndCert[1];
                         packageCertMap.put(packageName, cert);
+                    } else if (packageAndCert.length == 1
+                            && packageAndCert[0].equals(ADB_INSTALLER)) {
+                        packageCertMap.put(ADB_INSTALLER, INSTALLER_CERT_NOT_APPLICABLE);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index e224724..f90fab4 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -24,14 +24,15 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.integrity.model.RuleMetadata;
+import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.parser.RuleIndexingController;
 import com.android.server.integrity.parser.RuleMetadataParser;
 import com.android.server.integrity.parser.RuleParseException;
 import com.android.server.integrity.parser.RuleParser;
-import com.android.server.integrity.parser.RuleXmlParser;
+import com.android.server.integrity.serializer.RuleBinarySerializer;
 import com.android.server.integrity.serializer.RuleMetadataSerializer;
 import com.android.server.integrity.serializer.RuleSerializeException;
 import com.android.server.integrity.serializer.RuleSerializer;
-import com.android.server.integrity.serializer.RuleXmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -44,12 +45,9 @@
 public class IntegrityFileManager {
     private static final String TAG = "IntegrityFileManager";
 
-    // TODO: this is a prototype implementation of this class. Thus no tests are included.
-    //  Implementing rule indexing will likely overhaul this class and more tests should be included
-    //  then.
-
     private static final String METADATA_FILE = "metadata";
     private static final String RULES_FILE = "rules";
+    private static final String INDEXING_FILE = "indexing";
     private static final Object RULES_LOCK = new Object();
 
     private static IntegrityFileManager sInstance = null;
@@ -57,13 +55,17 @@
     private final RuleParser mRuleParser;
     private final RuleSerializer mRuleSerializer;
 
+    private final File mDataDir;
     // mRulesDir contains data of the actual rules currently stored.
     private final File mRulesDir;
     // mStagingDir is used to store the temporary rules / metadata during updating, since we want to
     // update rules atomically.
     private final File mStagingDir;
 
-    @Nullable private RuleMetadata mRuleMetadataCache;
+    @Nullable
+    private RuleMetadata mRuleMetadataCache;
+    @Nullable
+    private RuleIndexingController mRuleIndexingController;
 
     /** Get the singleton instance of this class. */
     public static synchronized IntegrityFileManager getInstance() {
@@ -75,8 +77,8 @@
 
     private IntegrityFileManager() {
         this(
-                new RuleXmlParser(),
-                new RuleXmlSerializer(),
+                new RuleBinaryParser(),
+                new RuleBinarySerializer(),
                 Environment.getDataSystemDirectory());
     }
 
@@ -84,11 +86,12 @@
     IntegrityFileManager(RuleParser ruleParser, RuleSerializer ruleSerializer, File dataDir) {
         mRuleParser = ruleParser;
         mRuleSerializer = ruleSerializer;
+        mDataDir = dataDir;
 
         mRulesDir = new File(dataDir, "integrity_rules");
         mStagingDir = new File(dataDir, "integrity_staging");
 
-        if (!mStagingDir.mkdirs() && mRulesDir.mkdirs()) {
+        if (!mStagingDir.mkdirs() || !mRulesDir.mkdirs()) {
             Slog.e(TAG, "Error creating staging and rules directory");
             // TODO: maybe throw an exception?
         }
@@ -101,6 +104,19 @@
                 Slog.e(TAG, "Error reading metadata file.", e);
             }
         }
+
+        updateRuleIndexingController();
+    }
+
+    /**
+     * Returns if the rules have been initialized.
+     *
+     * <p>Used to fail early if there are no rules (so we don't need to parse the apk at all).
+     */
+    public boolean initialized() {
+        return new File(mRulesDir, RULES_FILE).exists()
+                && new File(mRulesDir, METADATA_FILE).exists()
+                && new File(mRulesDir, INDEXING_FILE).exists();
     }
 
     /** Write rules to persistent storage. */
@@ -113,12 +129,18 @@
             // We don't consider this fatal so we continue execution.
         }
 
-        try (FileOutputStream fileOutputStream =
-                new FileOutputStream(new File(mStagingDir, RULES_FILE))) {
-            mRuleSerializer.serialize(rules, Optional.empty(), fileOutputStream);
+        try (FileOutputStream ruleFileOutputStream =
+                     new FileOutputStream(new File(mStagingDir, RULES_FILE));
+             FileOutputStream indexingFileOutputStream =
+                     new FileOutputStream(new File(mStagingDir, INDEXING_FILE))) {
+            mRuleSerializer.serialize(
+                    rules, Optional.empty(), ruleFileOutputStream, indexingFileOutputStream);
         }
 
         switchStagingRulesDir();
+
+        // Update object holding the indexing information.
+        updateRuleIndexingController();
     }
 
     /**
@@ -128,10 +150,15 @@
      */
     public List<Rule> readRules(AppInstallMetadata appInstallMetadata)
             throws IOException, RuleParseException {
-        // TODO: select rules by index
         synchronized (RULES_LOCK) {
+            // Try to identify indexes from the index file.
+            List<List<Integer>> ruleReadingIndexes =
+                    mRuleIndexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+            // Read the rules based on the index information.
+            // TODO(b/145493956): Provide the identified indexes to the rule reader.
             try (FileInputStream inputStream =
-                    new FileInputStream(new File(mRulesDir, RULES_FILE))) {
+                         new FileInputStream(new File(mRulesDir, RULES_FILE))) {
                 List<Rule> rules = mRuleParser.parse(inputStream);
                 return rules;
             }
@@ -146,7 +173,7 @@
 
     private void switchStagingRulesDir() throws IOException {
         synchronized (RULES_LOCK) {
-            File tmpDir = new File(Environment.getDataSystemDirectory(), "temp");
+            File tmpDir = new File(mDataDir, "temp");
 
             if (!(mRulesDir.renameTo(tmpDir)
                     && mStagingDir.renameTo(mRulesDir)
@@ -156,6 +183,17 @@
         }
     }
 
+    private void updateRuleIndexingController() {
+        File ruleIndexingFile = new File(mRulesDir, INDEXING_FILE);
+        if (ruleIndexingFile.exists()) {
+            try (FileInputStream inputStream = new FileInputStream(ruleIndexingFile)) {
+                mRuleIndexingController = new RuleIndexingController(inputStream);
+            } catch (Exception e) {
+                Slog.e(TAG, "Error parsing the rule indexing file.", e);
+            }
+        }
+    }
+
     private void writeMetadata(File directory, String ruleProvider, String version)
             throws IOException {
         mRuleMetadataCache = new RuleMetadata(ruleProvider, version);
diff --git a/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
new file mode 100644
index 0000000..52df89870
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/IndexingFileConstants.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.model;
+
+/**  A helper class containing special indexing file constants. */
+public final class IndexingFileConstants {
+    // The parsing time seems acceptable for this block size based on the tests in
+    // go/ic-rule-file-format.
+    public static final int INDEXING_BLOCK_SIZE = 100;
+
+    public static final String START_INDEXING_KEY = "START_KEY";
+    public static final String END_INDEXING_KEY = "END_KEY";
+}
diff --git a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
new file mode 100644
index 0000000..2c5b7d3
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+
+import com.android.server.integrity.IntegrityUtils;
+import com.android.server.integrity.model.BitInputStream;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Helper methods for reading standard data structures from {@link BitInputStream}.
+ */
+public class BinaryFileOperations {
+
+    /**
+     * Read an string value with the given size and hash status from a {@code BitInputStream}.
+     *
+     * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
+     * All hashed values are hex-encoded.
+     */
+    public static String getStringValue(BitInputStream bitInputStream) throws IOException {
+        boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
+        int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
+        return getStringValue(bitInputStream, valueSize, isHashedValue);
+    }
+
+    /**
+     * Read an string value with the given size and hash status from a {@code BitInputStream}.
+     *
+     * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
+     * All hashed values are hex-encoded.
+     */
+    public static String getStringValue(
+            BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
+            throws IOException {
+        if (!isHashedValue) {
+            StringBuilder value = new StringBuilder();
+            while (valueSize-- > 0) {
+                value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
+            }
+            return value.toString();
+        }
+        ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
+        while (valueSize-- > 0) {
+            byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
+        }
+        return IntegrityUtils.getHexDigest(byteBuffer.array());
+    }
+
+    /** Read an integer value from a {@code BitInputStream}. */
+    public static int getIntValue(BitInputStream bitInputStream) throws IOException {
+        return bitInputStream.getNext(/* numOfBits= */ 32);
+    }
+
+    /** Read an boolean value from a {@code BitInputStream}. */
+    public static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
+        return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 8f84abc..cbb6e4e 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -28,18 +28,19 @@
 import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
 import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
 
 import android.content.integrity.AtomicFormula;
 import android.content.integrity.CompoundFormula;
 import android.content.integrity.Formula;
 import android.content.integrity.Rule;
 
-import com.android.server.integrity.IntegrityUtils;
 import com.android.server.integrity.model.BitInputStream;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -145,33 +146,4 @@
                 throw new IllegalArgumentException(String.format("Unknown key: %d", key));
         }
     }
-
-    // Get value string from stream.
-    // If the value is not hashed, get its raw form directly.
-    // If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form.
-    // All hashed values are hex-encoded.
-    private static String getStringValue(
-            BitInputStream bitInputStream, int valueSize, boolean isHashedValue)
-            throws IOException {
-        if (!isHashedValue) {
-            StringBuilder value = new StringBuilder();
-            while (valueSize-- > 0) {
-                value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
-            }
-            return value.toString();
-        }
-        ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize);
-        while (valueSize-- > 0) {
-            byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF));
-        }
-        return IntegrityUtils.getHexDigest(byteBuffer.array());
-    }
-
-    private static int getIntValue(BitInputStream bitInputStream) throws IOException {
-        return bitInputStream.getNext(/* numOfBits= */ 32);
-    }
-
-    private static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException {
-        return bitInputStream.getNext(/* numOfBits= */ 1) == 1;
-    }
 }
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
new file mode 100644
index 0000000..b642fa6
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
+
+import android.content.integrity.AppInstallMetadata;
+
+import com.android.server.integrity.model.BitInputStream;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.TreeMap;
+
+/** Helper class to identify the necessary indexes that needs to be read. */
+public class RuleIndexingController {
+
+    private static TreeMap<String, Integer> sPackageNameBasedIndexes;
+    private static TreeMap<String, Integer> sAppCertificateBasedIndexes;
+    private static TreeMap<String, Integer> sUnindexedRuleIndexes;
+
+    /**
+     * Provide the indexing file to read and the object will be constructed by reading and
+     * identifying the indexes.
+     */
+    public RuleIndexingController(FileInputStream fileInputStream) throws IOException {
+        BitInputStream bitInputStream = new BitInputStream(fileInputStream);
+        sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream);
+        sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream);
+        sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream);
+    }
+
+    /**
+     * Returns a list of integers with the starting and ending bytes of the rules that needs to be
+     * read and evaluated.
+     */
+    public List<List<Integer>> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) {
+        // TODO(b/145493956): Identify and return the indexes that needs to be read.
+        return new ArrayList<>();
+    }
+
+    private TreeMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream)
+            throws IOException {
+        TreeMap<String, Integer> keyToIndexMap = new TreeMap<>();
+        while (bitInputStream.hasNext()) {
+            String key = getStringValue(bitInputStream);
+            int value = getIntValue(bitInputStream);
+
+            keyToIndexMap.put(key, value);
+
+            if (key == END_INDEXING_KEY) {
+                break;
+            }
+        }
+        return keyToIndexMap;
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java b/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
new file mode 100644
index 0000000..62815a9
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.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 com.android.server.integrity.serializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * An output stream that tracks the total number written bytes since construction and allows
+ * querying this value any time during the execution.
+ *
+ * This class is used for constructing the rule indexing.
+ */
+public class ByteTrackedOutputStream {
+
+    private static int sWrittenBytesCount;
+    private static OutputStream sOutputStream;
+
+    public ByteTrackedOutputStream(OutputStream outputStream) {
+        sWrittenBytesCount = 0;
+        sOutputStream = outputStream;
+    }
+
+    /**
+     * Writes the given bytes into the output stream provided in constructor and updates the
+     * total number of written bytes.
+     */
+    public void write(byte[] bytes) throws IOException {
+        sWrittenBytesCount += bytes.length;
+        sOutputStream.write(bytes);
+    }
+
+    /**
+     * Returns the total number of bytes written into the output stream at the requested time.
+     */
+    public int getWrittenBytesCount() {
+        return sWrittenBytesCount;
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 25defb0..b8791c3 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -27,6 +27,9 @@
 import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
 import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED;
 import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED;
 import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED;
@@ -36,6 +39,7 @@
 import android.content.integrity.Formula;
 import android.content.integrity.Rule;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.integrity.IntegrityUtils;
 import com.android.server.integrity.model.BitOutputStream;
 
@@ -46,6 +50,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.TreeMap;
 
 /** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
 public class RuleBinarySerializer implements RuleSerializer {
@@ -55,9 +60,9 @@
     public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
             throws RuleSerializeException {
         try {
-            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-            serialize(rules, formatVersion, byteArrayOutputStream);
-            return byteArrayOutputStream.toByteArray();
+            ByteArrayOutputStream rulesOutputStream = new ByteArrayOutputStream();
+            serialize(rules, formatVersion, rulesOutputStream, new ByteArrayOutputStream());
+            return rulesOutputStream.toByteArray();
         } catch (Exception e) {
             throw new RuleSerializeException(e.getMessage(), e);
         }
@@ -66,25 +71,45 @@
     // Get the byte representation for a list of rules, and write them to an output stream.
     @Override
     public void serialize(
-            List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+            List<Rule> rules,
+            Optional<Integer> formatVersion,
+            OutputStream rulesFileOutputStream,
+            OutputStream indexingFileOutputStream)
             throws RuleSerializeException {
         try {
             // Determine the indexing groups and the order of the rules within each indexed group.
-            Map<Integer, List<Rule>> indexedRules =
+            Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
                     RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
 
-            serializeRuleFileMetadata(formatVersion, outputStream);
+            ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
+                    new ByteTrackedOutputStream(rulesFileOutputStream);
 
-            serializeIndexedRules(indexedRules.get(PACKAGE_NAME_INDEXED), outputStream);
-            serializeIndexedRules(indexedRules.get(APP_CERTIFICATE_INDEXED), outputStream);
-            serializeIndexedRules(indexedRules.get(NOT_INDEXED), outputStream);
+            serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
+
+            Map<String, Integer> packageNameIndexes =
+                    serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED),
+                            ruleFileByteTrackedOutputStream);
+            indexingFileOutputStream.write(
+                    serializeIndexes(packageNameIndexes, /* isIndexed= */true));
+
+            Map<String, Integer> appCertificateIndexes =
+                    serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED),
+                            ruleFileByteTrackedOutputStream);
+            indexingFileOutputStream.write(
+                    serializeIndexes(appCertificateIndexes, /* isIndexed= */true));
+
+            Map<String, Integer> unindexedRulesIndexes =
+                    serializeRuleList(indexedRules.get(NOT_INDEXED),
+                            ruleFileByteTrackedOutputStream);
+            indexingFileOutputStream.write(
+                    serializeIndexes(unindexedRulesIndexes, /* isIndexed= */false));
         } catch (Exception e) {
             throw new RuleSerializeException(e.getMessage(), e);
         }
     }
 
-    private void serializeRuleFileMetadata(
-            Optional<Integer> formatVersion, OutputStream outputStream) throws IOException {
+    private void serializeRuleFileMetadata(Optional<Integer> formatVersion,
+            ByteTrackedOutputStream outputStream) throws IOException {
         int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
 
         BitOutputStream bitOutputStream = new BitOutputStream();
@@ -92,17 +117,33 @@
         outputStream.write(bitOutputStream.toByteArray());
     }
 
-    private void serializeIndexedRules(List<Rule> rules, OutputStream outputStream)
+    private Map<String, Integer> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
+            ByteTrackedOutputStream outputStream)
             throws IOException {
-        if (rules == null) {
-            return;
-        }
+        Preconditions.checkArgument(rulesMap != null,
+                "serializeRuleList should never be called with null rule list.");
+
         BitOutputStream bitOutputStream = new BitOutputStream();
-        for (Rule rule : rules) {
-            bitOutputStream.clear();
-            serializeRule(rule, bitOutputStream);
-            outputStream.write(bitOutputStream.toByteArray());
+        Map<String, Integer> indexMapping = new TreeMap();
+        int indexTracker = 0;
+
+        indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount());
+        for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
+            if (indexTracker >= INDEXING_BLOCK_SIZE) {
+                indexMapping.put(entry.getKey(), outputStream.getWrittenBytesCount());
+                indexTracker = 0;
+            }
+
+            for (Rule rule : entry.getValue()) {
+                bitOutputStream.clear();
+                serializeRule(rule, bitOutputStream);
+                outputStream.write(bitOutputStream.toByteArray());
+                indexTracker++;
+            }
         }
+        indexMapping.put(END_INDEXING_KEY, outputStream.getWrittenBytesCount());
+
+        return indexMapping;
     }
 
     private void serializeRule(Rule rule, BitOutputStream bitOutputStream) {
@@ -177,6 +218,33 @@
         }
     }
 
+    private byte[] serializeIndexes(Map<String, Integer> indexes, boolean isIndexed) {
+        BitOutputStream bitOutputStream = new BitOutputStream();
+
+        // Output the starting location of this indexing group.
+        serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */false,
+                bitOutputStream);
+        serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream);
+
+        // If the group is indexed, output the locations of the indexes.
+        if (isIndexed) {
+            for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
+                if (!entry.getKey().equals(START_INDEXING_KEY)
+                        && !entry.getKey().equals(END_INDEXING_KEY)) {
+                    serializeStringValue(entry.getKey(), /* isHashedValue= */false,
+                            bitOutputStream);
+                    serializeIntValue(entry.getValue(), bitOutputStream);
+                }
+            }
+        }
+
+        // Output the end location of this indexing group.
+        serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream);
+        serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream);
+
+        return bitOutputStream.toByteArray();
+    }
+
     private void serializeStringValue(
             String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
         if (value == null) {
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
index f9c7912..cbc365e 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
@@ -38,23 +38,19 @@
     private static final String DEFAULT_RULE_KEY = "N/A";
 
     /**
-     * Splits a given rule list into three indexing categories and returns a sorted list of rules
-     * per each index.
-     *
-     * The sorting guarantees an order based on the key but the rules that have the same key
-     * can be in arbitrary order. For example, given the rules of [package_name_a_rule_1,
-     * package_name_a_rule_2, package_name_b_rule_3, package_name_b_rule_4], the  method will
-     * guarantee that package_name_b rules (i.e., 3 and 4) will never come before package_name_a
-     * rules (i.e., 1 and 2). However, we do not care about the ordering between rule 1 and 2.
-     * We also do not care about the ordering between rule 3 and 4.
+     * Splits a given rule list into three indexing categories. Each rule category is returned as a
+     * TreeMap that is sorted by their indexing keys -- where keys correspond to package name for
+     * PACKAGE_NAME_INDEXED rules, app certificate for APP_CERTIFICATE_INDEXED rules and N/A for
+     * NOT_INDEXED rules.
      */
-    public static Map<Integer, List<Rule>> splitRulesIntoIndexBuckets(List<Rule> rules) {
+    public static Map<Integer, TreeMap<String, List<Rule>>> splitRulesIntoIndexBuckets(
+            List<Rule> rules) {
         if (rules == null) {
             throw new IllegalArgumentException(
                     "Index buckets cannot be created for null rule list.");
         }
 
-        Map<Integer, Map<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
+        Map<Integer, TreeMap<String, List<Rule>>> typeOrganizedRuleMap = new HashMap();
         typeOrganizedRuleMap.put(NOT_INDEXED, new TreeMap());
         typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new TreeMap());
         typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new TreeMap());
@@ -87,19 +83,7 @@
                     .add(rule);
         }
 
-        // Per indexing type, create the sorted rule set based on their key.
-        Map<Integer, List<Rule>> orderedListPerIndexingType = new HashMap<>();
-
-        for (Integer indexingKey : typeOrganizedRuleMap.keySet()) {
-            List<Rule> sortedRules = new ArrayList();
-            for (Map.Entry<String, List<Rule>> entry :
-                    typeOrganizedRuleMap.get(indexingKey).entrySet()) {
-                sortedRules.addAll(entry.getValue());
-            }
-            orderedListPerIndexingType.put(indexingKey, sortedRules);
-        }
-
-        return orderedListPerIndexingType;
+        return typeOrganizedRuleMap;
     }
 
     private static RuleIndexingDetails getIndexingDetails(Formula formula) {
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
index 4fcff65..2941856 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java
@@ -26,10 +26,14 @@
 public interface RuleSerializer {
 
     /** Serialize rules to an output stream */
-    void serialize(List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+    void serialize(
+            List<Rule> rules,
+            Optional<Integer> formatVersion,
+            OutputStream ruleFileOutputStream,
+            OutputStream indexingFileOutputStream)
             throws RuleSerializeException;
 
     /** Serialize rules to a ByteArray. */
-    byte[] serialize(List<Rule> rule, Optional<Integer> formatVersion)
+    byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
             throws RuleSerializeException;
 }
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index ebf6a2e..4194432 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -35,6 +35,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.TreeMap;
 
 /** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
 public class RuleXmlSerializer implements RuleSerializer {
@@ -55,12 +56,17 @@
 
     @Override
     public void serialize(
-            List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+            List<Rule> rules,
+            Optional<Integer> formatVersion,
+            OutputStream outputStream,
+            OutputStream indexingOutputStream)
             throws RuleSerializeException {
         try {
             XmlSerializer xmlSerializer = Xml.newSerializer();
             xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
             serializeRules(rules, xmlSerializer);
+
+            // TODO(b/145493956): Implement the indexing logic.
         } catch (Exception e) {
             throw new RuleSerializeException(e.getMessage(), e);
         }
@@ -84,7 +90,7 @@
             throws RuleSerializeException {
         try {
             // Determine the indexing groups and the order of the rules within each indexed group.
-            Map<Integer, List<Rule>> indexedRules =
+            Map<Integer, TreeMap<String, List<Rule>>> indexedRules =
                     RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
 
             // Write the XML formatted rules in order.
@@ -101,10 +107,13 @@
         }
     }
 
-    private void serializeRuleList(List<Rule> rules, XmlSerializer xmlSerializer)
+    private void serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
+            XmlSerializer xmlSerializer)
             throws IOException {
-        for (Rule rule : rules) {
-            serializeRule(rule, xmlSerializer);
+        for (Map.Entry<String, List<Rule>> entry : rulesMap.entrySet()) {
+            for (Rule rule : entry.getValue()) {
+                serializeRule(rule, xmlSerializer);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index f913ba3..d8561b6 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -74,7 +74,6 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
@@ -508,7 +507,7 @@
                     mHandler.sendEmptyMessage(UPDATE_LOW_POWER_MODE);
                     break;
                 case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
-                case TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
+                case TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED:
                     subscriptionOrCarrierConfigChanged();
                     break;
             }
@@ -2091,7 +2090,7 @@
             intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
             intentFilter.addAction(Intent.ACTION_SCREEN_ON);
             intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
-            intentFilter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+            intentFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
             mContext.registerReceiver(mBroadcastReceiver, intentFilter, null, this);
 
             mNetworkConnectivityHandler.registerNetworkCallbacks();
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 60ce1f4..01522739 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -25,10 +25,9 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * A helper class that handles operations in remote listeners.
@@ -61,7 +60,7 @@
     private int mLastReportedResult = RESULT_UNKNOWN;
 
     protected RemoteListenerHelper(Context context, Handler handler, String name) {
-        Preconditions.checkNotNull(name);
+        Objects.requireNonNull(name);
         mHandler = handler;
         mTag = name;
         mContext = context;
@@ -77,7 +76,7 @@
      * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
      */
     public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
-        Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
+        Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
         IBinder binder = listener.asBinder();
         synchronized (mListenerMap) {
             if (mListenerMap.containsKey(binder)) {
@@ -116,7 +115,7 @@
      * Remove GNSS data listener {@code listener}.
      */
     public void removeListener(@NonNull TListener listener) {
-        Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
+        Objects.requireNonNull(listener, "Attempted to remove a 'null' listener.");
         synchronized (mListenerMap) {
             mListenerMap.remove(listener.asBinder());
             if (mListenerMap.isEmpty()) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bcc4c1f..7233f34 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.READ_CONTACTS;
 import static android.content.Context.KEYGUARD_SERVICE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.UserHandle.USER_ALL;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
@@ -29,6 +30,7 @@
 import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
@@ -118,6 +120,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.LockscreenCredential;
+import com.android.internal.widget.RebootEscrowListener;
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -151,6 +154,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -211,7 +215,6 @@
     private final LockSettingsStrongAuth mStrongAuth;
     private final SynchronizedStrongAuthTracker mStrongAuthTracker;
 
-    private final LockPatternUtils mLockPatternUtils;
     private final NotificationManager mNotificationManager;
     private final UserManager mUserManager;
     private final IStorageManager mStorageManager;
@@ -222,11 +225,16 @@
 
     private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
 
+    private final RebootEscrowManager mRebootEscrowManager;
+
     private boolean mFirstCallToVold;
+
     // Current password metric for all users on the device. Updated when user unlocks
     // the device or changes password. Removed when user is stopped.
     @GuardedBy("this")
     final SparseArray<PasswordMetrics> mUserPasswordMetrics = new SparseArray<>();
+    @VisibleForTesting
+    protected boolean mHasSecureLockScreen;
 
     protected IGateKeeperService mGateKeeperService;
     protected IAuthSecret mAuthSecretService;
@@ -350,7 +358,7 @@
             return;
         }
         // Do not tie managed profile when work challenge is enabled
-        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+        if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
             return;
         }
         // Do not tie managed profile to parent when it's done already
@@ -433,10 +441,6 @@
             return ActivityManager.getService();
         }
 
-        public LockPatternUtils getLockPatternUtils() {
-            return new LockPatternUtils(mContext);
-        }
-
         public NotificationManager getNotificationManager() {
             return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         }
@@ -485,6 +489,11 @@
                     new PasswordSlotManager());
         }
 
+        public RebootEscrowManager getRebootEscrowManager(RebootEscrowManager.Callbacks callbacks,
+                LockSettingsStorage storage) {
+            return new RebootEscrowManager(mContext, callbacks, storage);
+        }
+
         public boolean hasEnrolledBiometrics(int userId) {
             BiometricManager bm = mContext.getSystemService(BiometricManager.class);
             return bm.hasEnrolledBiometrics(userId);
@@ -534,7 +543,6 @@
         mStrongAuth = injector.getStrongAuth();
         mActivityManager = injector.getActivityManager();
 
-        mLockPatternUtils = injector.getLockPatternUtils();
         mFirstCallToVold = true;
 
         IntentFilter filter = new IntentFilter();
@@ -553,6 +561,9 @@
 
         mSpManager = injector.getSyntheticPasswordManager(mStorage);
 
+        mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
+                mStorage);
+
         LocalServices.addService(LockSettingsInternal.class, new LocalService());
     }
 
@@ -779,10 +790,20 @@
             EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), "");  // SafetyNet
         }
         checkWritePermission(UserHandle.USER_SYSTEM);
+
+        mHasSecureLockScreen = mContext.getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN);
         migrateOldData();
         getGateKeeperService();
         mSpManager.initWeaverService();
-        // Find the AuthSecret HAL
+        getAuthSecretHal();
+        mDeviceProvisionedObserver.onSystemReady();
+        mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
+        // TODO: maybe skip this for split system user mode.
+        mStorage.prefetchUser(UserHandle.USER_SYSTEM);
+    }
+
+    private void getAuthSecretHal() {
         try {
             mAuthSecretService = IAuthSecret.getService();
         } catch (NoSuchElementException e) {
@@ -790,9 +811,6 @@
         } catch (RemoteException e) {
             Slog.w(TAG, "Failed to get AuthSecret HAL", e);
         }
-        mDeviceProvisionedObserver.onSystemReady();
-        // TODO: maybe skip this for split system user mode.
-        mStorage.prefetchUser(UserHandle.USER_SYSTEM);
     }
 
     private void migrateOldData() {
@@ -1018,6 +1036,11 @@
     }
 
     @Override
+    public boolean hasSecureLockScreen() {
+        return mHasSecureLockScreen;
+    }
+
+    @Override
     public boolean getSeparateProfileChallengeEnabled(int userId) {
         checkReadPermission(SEPARATE_PROFILE_CHALLENGE_KEY, userId);
         return getSeparateProfileChallengeEnabledInternal(userId);
@@ -1033,7 +1056,7 @@
     public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
             LockscreenCredential managedUserPassword) {
         checkWritePermission(userId);
-        if (!mLockPatternUtils.hasSecureLockScreen()) {
+        if (!mHasSecureLockScreen) {
             throw new UnsupportedOperationException(
                     "This operation requires secure lock screen feature.");
         }
@@ -1146,12 +1169,7 @@
 
     private String getStringUnchecked(String key, String defaultValue, int userId) {
         if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) {
-            long ident = Binder.clearCallingIdentity();
-            try {
-                return mLockPatternUtils.isLockPatternEnabled(userId) ? "1" : "0";
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
+            return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN ? "1" : "0";
         }
         if (userId == USER_FRP) {
             return null;
@@ -1401,7 +1419,7 @@
 
     private boolean tiedManagedProfileReadyToUnlock(UserInfo userInfo) {
         return userInfo.isManagedProfile()
-                && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id)
+                && !getSeparateProfileChallengeEnabledInternal(userInfo.id)
                 && mStorage.hasChildProfileLock(userInfo.id)
                 && mUserManager.isUserRunning(userInfo.id);
     }
@@ -1419,7 +1437,7 @@
                 continue;
             }
             final int managedUserId = profile.id;
-            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+            if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
                 continue;
             }
             try {
@@ -1459,7 +1477,7 @@
             final UserInfo profile = profiles.get(i);
             if (profile.isManagedProfile()) {
                 final int managedUserId = profile.id;
-                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+                if (getSeparateProfileChallengeEnabledInternal(managedUserId)) {
                     continue;
                 }
                 if (isSecure) {
@@ -1486,12 +1504,12 @@
 
     private boolean isManagedProfileWithUnifiedLock(int userId) {
         return mUserManager.getUserInfo(userId).isManagedProfile()
-                && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+                && !getSeparateProfileChallengeEnabledInternal(userId);
     }
 
     private boolean isManagedProfileWithSeparatedLock(int userId) {
         return mUserManager.getUserInfo(userId).isManagedProfile()
-                && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+                && getSeparateProfileChallengeEnabledInternal(userId);
     }
 
     /**
@@ -1564,7 +1582,7 @@
     public boolean setLockCredential(LockscreenCredential credential,
             LockscreenCredential savedCredential, int userId) {
 
-        if (!mLockPatternUtils.hasSecureLockScreen()) {
+        if (!mHasSecureLockScreen) {
             throw new UnsupportedOperationException(
                     "This operation requires secure lock screen feature");
         }
@@ -1594,8 +1612,8 @@
      */
     private boolean setLockCredentialInternal(LockscreenCredential credential,
             LockscreenCredential savedCredential, int userId, boolean isLockTiedToParent) {
-        Preconditions.checkNotNull(credential);
-        Preconditions.checkNotNull(savedCredential);
+        Objects.requireNonNull(credential);
+        Objects.requireNonNull(savedCredential);
         synchronized (mSpManager) {
             if (isSyntheticPasswordBasedCredentialLocked(userId)) {
                 return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId,
@@ -1871,7 +1889,7 @@
         for (UserInfo pi : profiles) {
             // Unlock managed profile with unified lock
             if (pi.isManagedProfile()
-                    && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
+                    && !getSeparateProfileChallengeEnabledInternal(pi.id)
                     && mStorage.hasChildProfileLock(pi.id)) {
                 try {
                     if (managedUserId == -1) {
@@ -2466,10 +2484,18 @@
 
     private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
         if (mInjector.isGsiRunning()) {
-            Slog.w(TAG, "AuthSecret disabled in GSI");
+            Slog.w(TAG, "Running in GSI; skipping calls to AuthSecret and RebootEscrow");
             return;
         }
 
+        mRebootEscrowManager.callToRebootEscrowIfNeeded(userId, auth.getVersion(),
+                auth.getSyntheticPassword());
+
+        callToAuthSecretIfNeeded(userId, auth);
+    }
+
+    private void callToAuthSecretIfNeeded(@UserIdInt int userId,
+            AuthenticationToken auth) {
         // Pass the primary user's auth secret to the HAL
         if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
             try {
@@ -2866,7 +2892,6 @@
         VerifyCredentialResponse response = authResult.gkResponse;
         AuthenticationToken auth = authResult.authToken;
 
-
         if (auth == null) {
             if (response == null
                     || response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR) {
@@ -3074,9 +3099,6 @@
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
 
         pw.println("Current lock settings service state:");
-
-        pw.println(String.format("SP Enabled = %b",
-                mLockPatternUtils.isSyntheticPasswordEnabled()));
         pw.println();
 
         pw.println("User State:");
@@ -3262,7 +3284,7 @@
         @Override
         public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
                 byte[] token, int userId) {
-            if (!mLockPatternUtils.hasSecureLockScreen()) {
+            if (!mHasSecureLockScreen) {
                 throw new UnsupportedOperationException(
                         "This operation requires secure lock screen feature.");
             }
@@ -3292,5 +3314,46 @@
             return LockSettingsService.this.getUserPasswordMetrics(userHandle);
         }
 
+        @Override
+        public void prepareRebootEscrow() {
+            if (!mRebootEscrowManager.prepareRebootEscrow()) {
+                return;
+            }
+            mStrongAuth.requireStrongAuth(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE, USER_ALL);
+        }
+
+        @Override
+        public void setRebootEscrowListener(RebootEscrowListener listener) {
+            mRebootEscrowManager.setRebootEscrowListener(listener);
+        }
+
+        @Override
+        public void clearRebootEscrow() {
+            if (!mRebootEscrowManager.clearRebootEscrow()) {
+                return;
+            }
+            mStrongAuth.noLongerRequireStrongAuth(STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE,
+                    USER_ALL);
+        }
+
+        @Override
+        public boolean armRebootEscrow() {
+            return mRebootEscrowManager.armRebootEscrowIfNeeded();
+        }
+    }
+
+    private class RebootEscrowCallbacks implements RebootEscrowManager.Callbacks {
+        @Override
+        public boolean isUserSecure(int userId) {
+            return LockSettingsService.this.isUserSecure(userId);
+        }
+
+        @Override
+        public void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId) {
+            SyntheticPasswordManager.AuthenticationToken
+                    authToken = new SyntheticPasswordManager.AuthenticationToken(spVersion);
+            authToken.recreateDirectly(syntheticPassword);
+            onCredentialVerified(authToken, CHALLENGE_NONE, 0, null, userId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 3dab3ce..fec0189 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -81,6 +81,8 @@
     private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
     private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
 
+    private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+
     private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
 
     private static final Object DEFAULT = new Object();
@@ -261,6 +263,22 @@
         return hasFile(getChildProfileLockFile(userId));
     }
 
+    public void writeRebootEscrow(int userId, byte[] rebootEscrow) {
+        writeFile(getRebootEscrowFile(userId), rebootEscrow);
+    }
+
+    public byte[] readRebootEscrow(int userId) {
+        return readFile(getRebootEscrowFile(userId));
+    }
+
+    public boolean hasRebootEscrow(int userId) {
+        return hasFile(getRebootEscrowFile(userId));
+    }
+
+    public void removeRebootEscrow(int userId) {
+        deleteFile(getRebootEscrowFile(userId));
+    }
+
     public boolean hasPassword(int userId) {
         return hasFile(getLockPasswordFilename(userId));
     }
@@ -384,6 +402,11 @@
         return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
     }
 
+    @VisibleForTesting
+    String getRebootEscrowFile(int userId) {
+        return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
+    }
+
     private String getLockCredentialFilePathForUser(int userId, String basename) {
         String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
                         SYSTEM_DIRECTORY;
@@ -479,18 +502,10 @@
         if (parentInfo == null) {
             // This user owns its lock settings files - safe to delete them
             synchronized (mFileWriteLock) {
-                String name = getLockPasswordFilename(userId);
-                File file = new File(name);
-                if (file.exists()) {
-                    file.delete();
-                    mCache.putFile(name, null);
-                }
-                name = getLockPatternFilename(userId);
-                file = new File(name);
-                if (file.exists()) {
-                    file.delete();
-                    mCache.putFile(name, null);
-                }
+                deleteFilesAndRemoveCache(
+                        getLockPasswordFilename(userId),
+                        getLockPatternFilename(userId),
+                        getRebootEscrowFile(userId));
             }
         } else {
             // Managed profile
@@ -512,6 +527,16 @@
         }
     }
 
+    private void deleteFilesAndRemoveCache(String... names) {
+        for (String name : names) {
+            File file = new File(name);
+            if (file.exists()) {
+                file.delete();
+                mCache.putFile(name, null);
+            }
+        }
+    }
+
     @VisibleForTesting
     void closeDatabase() {
         mOpenHelper.close();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index a84306c..91cf53e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -16,10 +16,8 @@
 
 package com.android.server.locksettings;
 
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
-        .STRONG_AUTH_NOT_REQUIRED;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker
-        .STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
@@ -50,6 +48,7 @@
     private static final int MSG_UNREGISTER_TRACKER = 3;
     private static final int MSG_REMOVE_USER = 4;
     private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5;
+    private static final int MSG_NO_LONGER_REQUIRE_STRONG_AUTH = 6;
 
     private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
             "LockSettingsStrongAuth.timeoutForUser";
@@ -109,6 +108,26 @@
         }
     }
 
+    private void handleNoLongerRequireStrongAuth(int strongAuthReason, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+                int key = mStrongAuthForUser.keyAt(i);
+                handleNoLongerRequireStrongAuthOneUser(strongAuthReason, key);
+            }
+        } else {
+            handleNoLongerRequireStrongAuthOneUser(strongAuthReason, userId);
+        }
+    }
+
+    private void handleNoLongerRequireStrongAuthOneUser(int strongAuthReason, int userId) {
+        int oldValue = mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags);
+        int newValue = oldValue & ~strongAuthReason;
+        if (oldValue != newValue) {
+            mStrongAuthForUser.put(userId, newValue);
+            notifyStrongAuthTrackers(newValue, userId);
+        }
+    }
+
     private void handleRemoveUser(int userId) {
         int index = mStrongAuthForUser.indexOfKey(userId);
         if (index >= 0) {
@@ -174,6 +193,16 @@
         }
     }
 
+    void noLongerRequireStrongAuth(int strongAuthReason, int userId) {
+        if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
+            mHandler.obtainMessage(MSG_NO_LONGER_REQUIRE_STRONG_AUTH, strongAuthReason,
+                    userId).sendToTarget();
+        } else {
+            throw new IllegalArgumentException(
+                    "userId must be an explicit user id or USER_ALL");
+        }
+    }
+
     public void reportUnlock(int userId) {
         requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
     }
@@ -216,6 +245,9 @@
                 case MSG_SCHEDULE_STRONG_AUTH_TIMEOUT:
                     handleScheduleStrongAuthTimeout(msg.arg1);
                     break;
+                case MSG_NO_LONGER_REQUIRE_STRONG_AUTH:
+                    handleNoLongerRequireStrongAuth(msg.arg1, msg.arg2);
+                    break;
             }
         }
     };
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowData.java b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
new file mode 100644
index 0000000..aee608e
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowData.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Holds the data necessary to complete a reboot escrow of the Synthetic Password.
+ */
+class RebootEscrowData {
+    /**
+     * This is the current version of the escrow data format. This should be incremented if the
+     * format on disk is changed.
+     */
+    private static final int CURRENT_VERSION = 1;
+
+    /** The secret key will be of this format. */
+    private static final String KEY_ALGO = "AES";
+
+    /** The key size used for encrypting the reboot escrow data. */
+    private static final int KEY_SIZE_BITS = 256;
+
+    /** The algorithm used for the encryption of the key blob. */
+    private static final String CIPHER_ALGO = "AES/GCM/NoPadding";
+
+    private RebootEscrowData(byte spVersion, byte[] iv, byte[] syntheticPassword, byte[] blob,
+            byte[] key) {
+        mSpVersion = spVersion;
+        mIv = iv;
+        mSyntheticPassword = syntheticPassword;
+        mBlob = blob;
+        mKey = key;
+    }
+
+    private final byte mSpVersion;
+    private final byte[] mIv;
+    private final byte[] mSyntheticPassword;
+    private final byte[] mBlob;
+    private final byte[] mKey;
+
+    public byte getSpVersion() {
+        return mSpVersion;
+    }
+
+    public byte[] getIv() {
+        return mIv;
+    }
+
+    public byte[] getSyntheticPassword() {
+        return mSyntheticPassword;
+    }
+
+    public byte[] getBlob() {
+        return mBlob;
+    }
+
+    public byte[] getKey() {
+        return mKey;
+    }
+
+    static SecretKeySpec fromKeyBytes(byte[] keyBytes) {
+        return new SecretKeySpec(keyBytes, KEY_ALGO);
+    }
+
+    static RebootEscrowData fromEncryptedData(SecretKeySpec keySpec, byte[] blob)
+            throws IOException {
+        Preconditions.checkNotNull(keySpec);
+        Preconditions.checkNotNull(blob);
+
+        DataInputStream dis = new DataInputStream(new ByteArrayInputStream(blob));
+        int version = dis.readInt();
+        if (version != CURRENT_VERSION) {
+            throw new IOException("Unsupported version " + version);
+        }
+
+        byte spVersion = dis.readByte();
+
+        int ivSize = dis.readInt();
+        if (ivSize < 0 || ivSize > 32) {
+            throw new IOException("IV out of range: " + ivSize);
+        }
+        byte[] iv = new byte[ivSize];
+        dis.readFully(iv);
+
+        int cipherTextSize = dis.readInt();
+        if (cipherTextSize < 0) {
+            throw new IOException("Invalid cipher text size: " + cipherTextSize);
+        }
+
+        byte[] cipherText = new byte[cipherTextSize];
+        dis.readFully(cipherText);
+
+        final byte[] syntheticPassword;
+        try {
+            Cipher c = Cipher.getInstance(CIPHER_ALGO);
+            c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
+            syntheticPassword = c.doFinal(cipherText);
+        } catch (NoSuchAlgorithmException | InvalidKeyException | BadPaddingException
+                | IllegalBlockSizeException | NoSuchPaddingException
+                | InvalidAlgorithmParameterException e) {
+            throw new IOException("Could not decrypt ciphertext", e);
+        }
+
+        return new RebootEscrowData(spVersion, iv, syntheticPassword, blob, keySpec.getEncoded());
+    }
+
+    static RebootEscrowData fromSyntheticPassword(byte spVersion, byte[] syntheticPassword)
+            throws IOException {
+        Preconditions.checkNotNull(syntheticPassword);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        DataOutputStream dos = new DataOutputStream(bos);
+
+        final SecretKey secretKey;
+        try {
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGO);
+            keyGenerator.init(KEY_SIZE_BITS, new SecureRandom());
+            secretKey = keyGenerator.generateKey();
+        } catch (NoSuchAlgorithmException e) {
+            throw new IOException("Could not generate new secret key", e);
+        }
+
+        final byte[] cipherText;
+        final byte[] iv;
+        try {
+            Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
+            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+            cipherText = cipher.doFinal(syntheticPassword);
+            iv = cipher.getIV();
+        } catch (NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException
+                | NoSuchPaddingException | InvalidKeyException e) {
+            throw new IOException("Could not encrypt reboot escrow data", e);
+        }
+
+        dos.writeInt(CURRENT_VERSION);
+        dos.writeByte(spVersion);
+        dos.writeInt(iv.length);
+        dos.write(iv);
+        dos.writeInt(cipherText.length);
+        dos.write(cipherText);
+
+        return new RebootEscrowData(spVersion, iv, syntheticPassword, bos.toByteArray(),
+                secretKey.getEncoded());
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
new file mode 100644
index 0000000..d2e54f9
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -0,0 +1,275 @@
+/*
+ * 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.locksettings;
+
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.RebootEscrowListener;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.crypto.spec.SecretKeySpec;
+
+class RebootEscrowManager {
+    private static final String TAG = "RebootEscrowManager";
+
+    /**
+     * Used to track when the reboot escrow is wanted. Set to false when mRebootEscrowReady is
+     * true.
+     */
+    private final AtomicBoolean mRebootEscrowWanted = new AtomicBoolean(false);
+
+    /** Used to track when reboot escrow is ready. */
+    private boolean mRebootEscrowReady;
+
+    /** Notified when mRebootEscrowReady changes. */
+    private RebootEscrowListener mRebootEscrowListener;
+
+    /**
+     * Stores the reboot escrow data between when it's supplied and when
+     * {@link #armRebootEscrowIfNeeded()} is called.
+     */
+    private RebootEscrowData mPendingRebootEscrowData;
+
+    private final UserManager mUserManager;
+
+    private final Injector mInjector;
+
+    private final LockSettingsStorage mStorage;
+
+    private final Callbacks mCallbacks;
+
+    interface Callbacks {
+        boolean isUserSecure(int userId);
+        void onRebootEscrowRestored(byte spVersion, byte[] syntheticPassword, int userId);
+    }
+
+    static class Injector {
+        protected Context mContext;
+
+        Injector(Context context) {
+            mContext = context;
+        }
+
+        public Context getContext() {
+            return mContext;
+        }
+        public UserManager getUserManager() {
+            return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        }
+
+        public @Nullable IRebootEscrow getRebootEscrow() {
+            try {
+                return IRebootEscrow.Stub.asInterface(ServiceManager.getService(
+                        "android.hardware.rebootescrow.IRebootEscrow/default"));
+            } catch (NoSuchElementException e) {
+                Slog.i(TAG, "Device doesn't implement RebootEscrow HAL");
+            }
+            return null;
+        }
+    }
+
+    RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
+        this(new Injector(context), callbacks, storage);
+    }
+
+    @VisibleForTesting
+    RebootEscrowManager(Injector injector, Callbacks callbacks,
+            LockSettingsStorage storage) {
+        mInjector = injector;
+        mCallbacks = callbacks;
+        mStorage = storage;
+        mUserManager = injector.getUserManager();
+    }
+
+    void loadRebootEscrowDataIfAvailable() {
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            return;
+        }
+
+        final SecretKeySpec escrowKey;
+        try {
+            byte[] escrowKeyBytes = rebootEscrow.retrieveKey();
+            if (escrowKeyBytes == null) {
+                return;
+            } else if (escrowKeyBytes.length != 32) {
+                Slog.e(TAG, "IRebootEscrow returned key of incorrect size "
+                        + escrowKeyBytes.length);
+                return;
+            }
+
+            // Make sure we didn't get the null key.
+            int zero = 0;
+            for (int i = 0; i < escrowKeyBytes.length; i++) {
+                zero |= escrowKeyBytes[i];
+            }
+            if (zero == 0) {
+                Slog.w(TAG, "IRebootEscrow returned an all-zeroes key");
+                return;
+            }
+
+            // Overwrite the existing key with the null key
+            rebootEscrow.storeKey(new byte[32]);
+
+            escrowKey = RebootEscrowData.fromKeyBytes(escrowKeyBytes);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not retrieve escrow data");
+            return;
+        }
+
+        List<UserInfo> users = mUserManager.getUsers();
+        for (UserInfo user : users) {
+            if (mCallbacks.isUserSecure(user.id)) {
+                restoreRebootEscrowForUser(user.id, escrowKey);
+            }
+        }
+    }
+
+    private void restoreRebootEscrowForUser(@UserIdInt int userId, SecretKeySpec escrowKey) {
+        if (!mStorage.hasRebootEscrow(userId)) {
+            return;
+        }
+
+        try {
+            byte[] blob = mStorage.readRebootEscrow(userId);
+            mStorage.removeRebootEscrow(userId);
+
+            RebootEscrowData escrowData = RebootEscrowData.fromEncryptedData(escrowKey, blob);
+
+            mCallbacks.onRebootEscrowRestored(escrowData.getSpVersion(),
+                    escrowData.getSyntheticPassword(), userId);
+        } catch (IOException e) {
+            Slog.w(TAG, "Could not load reboot escrow data for user " + userId, e);
+        }
+    }
+
+    void callToRebootEscrowIfNeeded(@UserIdInt int userId, byte spVersion,
+            byte[] syntheticPassword) {
+        if (!mRebootEscrowWanted.compareAndSet(true, false)) {
+            return;
+        }
+
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            setRebootEscrowReady(false);
+            return;
+        }
+
+        final RebootEscrowData escrowData;
+        try {
+            escrowData = RebootEscrowData.fromSyntheticPassword(spVersion, syntheticPassword);
+        } catch (IOException e) {
+            setRebootEscrowReady(false);
+            Slog.w(TAG, "Could not escrow reboot data", e);
+            return;
+        }
+
+        mPendingRebootEscrowData = escrowData;
+        mStorage.writeRebootEscrow(userId, escrowData.getBlob());
+
+        setRebootEscrowReady(true);
+    }
+
+    private void clearRebootEscrowIfNeeded() {
+        mRebootEscrowWanted.set(false);
+        setRebootEscrowReady(false);
+
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            return;
+        }
+
+        try {
+            rebootEscrow.storeKey(new byte[32]);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not call RebootEscrow HAL to shred key");
+        }
+
+        List<UserInfo> users = mUserManager.getUsers();
+        for (UserInfo user : users) {
+            mStorage.removeRebootEscrow(user.id);
+        }
+    }
+
+    boolean armRebootEscrowIfNeeded() {
+        if (!mRebootEscrowReady) {
+            return false;
+        }
+
+        IRebootEscrow rebootEscrow = mInjector.getRebootEscrow();
+        if (rebootEscrow == null) {
+            return false;
+        }
+
+        RebootEscrowData escrowData = mPendingRebootEscrowData;
+        if (escrowData == null) {
+            return false;
+        }
+
+        boolean armedRebootEscrow = false;
+        try {
+            rebootEscrow.storeKey(escrowData.getKey());
+            armedRebootEscrow = true;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed escrow secret to RebootEscrow HAL", e);
+        }
+        return armedRebootEscrow;
+    }
+
+    private void setRebootEscrowReady(boolean ready) {
+        if (mRebootEscrowReady != ready) {
+            mRebootEscrowListener.onPreparedForReboot(ready);
+        }
+        mRebootEscrowReady = ready;
+    }
+
+    boolean prepareRebootEscrow() {
+        if (mInjector.getRebootEscrow() == null) {
+            return false;
+        }
+
+        clearRebootEscrowIfNeeded();
+        mRebootEscrowWanted.set(true);
+        return true;
+    }
+
+    boolean clearRebootEscrow() {
+        if (mInjector.getRebootEscrow() == null) {
+            return false;
+        }
+
+        clearRebootEscrowIfNeeded();
+        return true;
+    }
+
+    void setRebootEscrowListener(RebootEscrowListener listener) {
+        mRebootEscrowListener = listener;
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index c53647d..b726e57 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -38,7 +38,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
@@ -57,6 +56,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.Set;
 
 
@@ -228,8 +228,8 @@
          * by {@link #setEscrowData} before calling this.
          */
         public void recreateFromEscrow(byte[] escrowSplit0) {
-            Preconditions.checkNotNull(mEscrowSplit1);
-            Preconditions.checkNotNull(mEncryptedEscrowSplit0);
+            Objects.requireNonNull(mEscrowSplit1);
+            Objects.requireNonNull(mEncryptedEscrowSplit0);
             recreate(escrowSplit0, mEscrowSplit1);
         }
 
@@ -284,6 +284,14 @@
         public byte[] getSyntheticPassword() {
             return mSyntheticPassword;
         }
+
+        /**
+         * Returns the version of this AuthenticationToken for use with reconstructing
+         * this with a synthetic password version.
+         */
+        public byte getVersion() {
+            return mVersion;
+        }
     }
 
     static class PasswordData {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 1b14ce2..383d5cf 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -75,6 +75,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -302,8 +303,8 @@
         checkRecoverKeyStorePermission();
         rootCertificateAlias =
                 mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
-        Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
-        Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
+        Objects.requireNonNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
+        Objects.requireNonNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
 
         SigXml sigXml;
         try {
@@ -393,7 +394,7 @@
      */
     public void setRecoveryStatus(@NonNull String alias, int status) throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(alias, "alias is null");
+        Objects.requireNonNull(alias, "alias is null");
         long updatedRows = mDatabase.setRecoveryStatus(Binder.getCallingUid(), alias, status);
         if (updatedRows < 0) {
             throw new ServiceSpecificException(
@@ -424,7 +425,7 @@
             @NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(secretTypes, "secretTypes is null");
+        Objects.requireNonNull(secretTypes, "secretTypes is null");
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
 
@@ -556,11 +557,11 @@
         checkRecoverKeyStorePermission();
         rootCertificateAlias =
                 mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
-        Preconditions.checkNotNull(sessionId, "invalid session");
-        Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
-        Preconditions.checkNotNull(vaultParams, "vaultParams is null");
-        Preconditions.checkNotNull(vaultChallenge, "vaultChallenge is null");
-        Preconditions.checkNotNull(secrets, "secrets is null");
+        Objects.requireNonNull(sessionId, "invalid session");
+        Objects.requireNonNull(verifierCertPath, "verifierCertPath is null");
+        Objects.requireNonNull(vaultParams, "vaultParams is null");
+        Objects.requireNonNull(vaultChallenge, "vaultChallenge is null");
+        Objects.requireNonNull(secrets, "secrets is null");
         CertPath certPath;
         try {
             certPath = verifierCertPath.getCertPath();
@@ -666,13 +667,13 @@
      */
     public void closeSession(@NonNull String sessionId) throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(sessionId, "invalid session");
+        Objects.requireNonNull(sessionId, "invalid session");
         mRecoverySessionStorage.remove(Binder.getCallingUid(), sessionId);
     }
 
     public void removeKey(@NonNull String alias) throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(alias, "alias is null");
+        Objects.requireNonNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
@@ -711,7 +712,7 @@
     public String generateKeyWithMetadata(@NonNull String alias, @Nullable byte[] metadata)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(alias, "alias is null");
+        Objects.requireNonNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
 
@@ -771,8 +772,8 @@
     public @Nullable String importKeyWithMetadata(@NonNull String alias, @NonNull byte[] keyBytes,
             @Nullable byte[] metadata) throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(alias, "alias is null");
-        Preconditions.checkNotNull(keyBytes, "keyBytes is null");
+        Objects.requireNonNull(alias, "alias is null");
+        Objects.requireNonNull(keyBytes, "keyBytes is null");
         if (keyBytes.length != RecoverableKeyGenerator.KEY_SIZE_BITS / Byte.SIZE) {
             Log.e(TAG, "The given key for import doesn't have the required length "
                     + RecoverableKeyGenerator.KEY_SIZE_BITS);
@@ -816,7 +817,7 @@
      */
     public @Nullable String getKey(@NonNull String alias) throws RemoteException {
         checkRecoverKeyStorePermission();
-        Preconditions.checkNotNull(alias, "alias is null");
+        Objects.requireNonNull(alias, "alias is null");
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
         return getAlias(userId, uid, alias);
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index f11b70e..115155c 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -108,5 +108,8 @@
         // TODO: Remove this when MediaRouter2ServiceImpl notifies clients of session changes.
         void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
                 @NonNull RouteSessionInfo sessionInfo);
+        // TODO: Call this when service actually notifies of session release.
+        void onSessionReleased(@NonNull MediaRoute2Provider provider,
+                @NonNull RouteSessionInfo sessionInfo);
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index 423001f..f8d8f9f 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -286,6 +286,11 @@
         if (mActiveConnection != connection) {
             return;
         }
+        if (sessionInfo != null) {
+            sessionInfo = new RouteSessionInfo.Builder(sessionInfo)
+                    .setProviderId(getUniqueId())
+                    .build();
+        }
         mCallback.onSessionCreated(this, sessionInfo, requestId);
     }
 
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 0bdcc97..562a720 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -79,7 +79,6 @@
     @GuardedBy("mLock")
     private int mCurrentUserId = -1;
 
-
     MediaRouter2ServiceImpl(Context context) {
         mContext = context;
     }
@@ -89,17 +88,23 @@
         final int uid = Binder.getCallingUid();
         final int userId = UserHandle.getUserId(uid);
 
-        Collection<MediaRoute2Info> systemRoutes;
-        synchronized (mLock) {
-            UserRecord userRecord = mUserRecords.get(userId);
-            if (userRecord == null) {
-                userRecord = new UserRecord(userId);
-                mUserRecords.put(userId, userRecord);
-                initializeUserLocked(userRecord);
+        final long token = Binder.clearCallingIdentity();
+        try {
+            Collection<MediaRoute2Info> systemRoutes;
+            synchronized (mLock) {
+                UserRecord userRecord = getOrCreateUserRecordLocked(userId);
+                MediaRoute2ProviderInfo providerInfo =
+                        userRecord.mHandler.mSystemProvider.getProviderInfo();
+                if (providerInfo != null) {
+                    systemRoutes = providerInfo.getRoutes();
+                } else {
+                    systemRoutes = Collections.emptyList();
+                }
             }
-            systemRoutes = userRecord.mHandler.mSystemProvider.getProviderInfo().getRoutes();
+            return new ArrayList<>(systemRoutes);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
-        return new ArrayList<>(systemRoutes);
     }
 
     public void registerClient(@NonNull IMediaRouter2Client client,
@@ -233,6 +238,19 @@
         }
     }
 
+    public void releaseSession(IMediaRouter2Client client, String uniqueSessionId) {
+        Objects.requireNonNull(client, "client must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                releaseSessionLocked(client, uniqueSessionId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     public void sendControlRequest(@NonNull IMediaRouter2Client client,
             @NonNull MediaRoute2Info route, @NonNull Intent request) {
         Objects.requireNonNull(client, "client must not be null");
@@ -387,12 +405,7 @@
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = client.asBinder();
         if (mAllClientRecords.get(binder) == null) {
-            UserRecord userRecord = mUserRecords.get(userId);
-            if (userRecord == null) {
-                userRecord = new UserRecord(userId);
-                mUserRecords.put(userId, userRecord);
-                initializeUserLocked(userRecord);
-            }
+            UserRecord userRecord = getOrCreateUserRecordLocked(userId);
             Client2Record clientRecord = new Client2Record(userRecord, client, uid, pid,
                     packageName, trusted);
             try {
@@ -477,6 +490,18 @@
         }
     }
 
+    private void releaseSessionLocked(@NonNull IMediaRouter2Client client, String uniqueSessionId) {
+        final IBinder binder = client.asBinder();
+        final Client2Record clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::releaseSessionOnHandler,
+                            clientRecord.mUserRecord.mHandler,
+                            clientRecord, uniqueSessionId));
+        }
+    }
+
     private void setControlCategoriesLocked(Client2Record clientRecord, List<String> categories) {
         if (clientRecord != null) {
             if (clientRecord.mControlCategories.equals(categories)) {
@@ -531,12 +556,7 @@
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
         if (managerRecord == null) {
-            boolean newUser = false;
-            UserRecord userRecord = mUserRecords.get(userId);
-            if (userRecord == null) {
-                userRecord = new UserRecord(userId);
-                newUser = true;
-            }
+            UserRecord userRecord = getOrCreateUserRecordLocked(userId);
             managerRecord = new ManagerRecord(userRecord, manager, uid, pid, packageName, trusted);
             try {
                 binder.linkToDeath(managerRecord, 0);
@@ -544,11 +564,6 @@
                 throw new RuntimeException("Media router manager died prematurely.", ex);
             }
 
-            if (newUser) {
-                mUserRecords.put(userId, userRecord);
-                initializeUserLocked(userRecord);
-            }
-
             userRecord.mManagerRecords.add(managerRecord);
             mAllManagerRecords.put(binder, managerRecord);
 
@@ -636,14 +651,17 @@
         return sessionInfos;
     }
 
-    private void initializeUserLocked(UserRecord userRecord) {
-        if (DEBUG) {
-            Slog.d(TAG, userRecord + ": Initialized");
+    private UserRecord getOrCreateUserRecordLocked(int userId) {
+        UserRecord userRecord = mUserRecords.get(userId);
+        if (userRecord == null) {
+            userRecord = new UserRecord(userId);
+            mUserRecords.put(userId, userRecord);
+            if (userId == mCurrentUserId) {
+                userRecord.mHandler.sendMessage(
+                        obtainMessage(UserHandler::start, userRecord.mHandler));
+            }
         }
-        if (userRecord.mUserId == mCurrentUserId) {
-            userRecord.mHandler.sendMessage(
-                    obtainMessage(UserHandler::start, userRecord.mHandler));
-        }
+        return userRecord;
     }
 
     private void disposeUserIfNeededLocked(UserRecord userRecord) {
@@ -835,25 +853,32 @@
 
         @Override
         public void onProviderStateChanged(@NonNull MediaRoute2Provider provider) {
-            sendMessage(PooledLambda.obtainMessage(UserHandler::updateProvider, this, provider));
+            sendMessage(PooledLambda.obtainMessage(UserHandler::onProviderStateChangedOnHandler,
+                    this, provider));
         }
 
         @Override
         public void onSessionCreated(@NonNull MediaRoute2Provider provider,
                 @Nullable RouteSessionInfo sessionInfo, long requestId) {
-            sendMessage(PooledLambda.obtainMessage(UserHandler::handleCreateSessionResultOnHandler,
+            sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionCreatedOnHandler,
                     this, provider, sessionInfo, requestId));
         }
 
         @Override
         public void onSessionInfoChanged(@NonNull MediaRoute2Provider provider,
                 @NonNull RouteSessionInfo sessionInfo) {
-            sendMessage(PooledLambda.obtainMessage(UserHandler::updateSession,
+            sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionInfoChangedOnHandler,
+                    this, provider, sessionInfo));
+        }
+
+        @Override
+        public void onSessionReleased(MediaRoute2Provider provider, RouteSessionInfo sessionInfo) {
+            sendMessage(PooledLambda.obtainMessage(UserHandler::onSessionReleasedOnHandler,
                     this, provider, sessionInfo));
         }
 
         //TODO: notify session info updates
-        private void updateProvider(MediaRoute2Provider provider) {
+        private void onProviderStateChangedOnHandler(MediaRoute2Provider provider) {
             int providerIndex = getProviderInfoIndex(provider.getUniqueId());
             MediaRoute2ProviderInfo providerInfo = provider.getProviderInfo();
             MediaRoute2ProviderInfo prevInfo =
@@ -975,7 +1000,7 @@
             if (provider == null) {
                 return;
             }
-            provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId, providerId), route);
+            provider.selectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
         }
 
         private void deselectRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -991,8 +1016,7 @@
             if (provider == null) {
                 return;
             }
-            provider.deselectRoute(
-                    RouteSessionInfo.getSessionId(uniqueSessionId, providerId), route);
+            provider.deselectRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
         }
 
         private void transferToRouteOnHandler(@NonNull Client2Record clientRecord,
@@ -1008,8 +1032,7 @@
             if (provider == null) {
                 return;
             }
-            provider.transferToRoute(
-                    RouteSessionInfo.getSessionId(uniqueSessionId, providerId), route);
+            provider.transferToRoute(RouteSessionInfo.getSessionId(uniqueSessionId), route);
         }
 
         private boolean checkArgumentsForSessionControl(@NonNull Client2Record clientRecord,
@@ -1040,20 +1063,57 @@
                 return false;
             }
 
-            try {
-                RouteSessionInfo.getSessionId(uniqueSessionId, providerId);
-            } catch (Exception ex) {
+            final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+            if (sessionId == null) {
                 Slog.w(TAG, "Failed to get int session id from unique session id. "
-                        + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId);
+                        + "uniqueSessionId=" + uniqueSessionId);
                 return false;
             }
 
             return true;
         }
 
-        private void handleCreateSessionResultOnHandler(
-                @NonNull MediaRoute2Provider provider, @Nullable RouteSessionInfo sessionInfo,
-                long requestId) {
+        private void releaseSessionOnHandler(@NonNull Client2Record clientRecord,
+                String uniqueSessionId) {
+            if (TextUtils.isEmpty(uniqueSessionId)) {
+                Slog.w(TAG, "Ignoring releasing session with empty unique session ID.");
+                return;
+            }
+
+            final Client2Record matchingRecord = mSessionToClientMap.get(uniqueSessionId);
+            if (matchingRecord != clientRecord) {
+                Slog.w(TAG, "Ignoring releasing session from non-matching client."
+                        + " packageName=" + clientRecord.mPackageName
+                        + " uniqueSessionId=" + uniqueSessionId);
+                return;
+            }
+
+            final String providerId = RouteSessionInfo.getProviderId(uniqueSessionId);
+            if (providerId == null) {
+                Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
+                        + "uniqueSessionId=" + uniqueSessionId);
+                return;
+            }
+
+            final Integer sessionId = RouteSessionInfo.getSessionId(uniqueSessionId);
+            if (sessionId == null) {
+                Slog.w(TAG, "Ignoring releasing session with invalid unique session ID. "
+                        + "uniqueSessionId=" + uniqueSessionId + " providerId=" + providerId);
+                return;
+            }
+
+            final MediaRoute2Provider provider = findProvider(providerId);
+            if (provider == null) {
+                Slog.w(TAG, "Ignoring releasing session since no provider found for given "
+                        + "providerId=" + providerId);
+                return;
+            }
+
+            provider.releaseSession(sessionId);
+        }
+
+        private void onSessionCreatedOnHandler(@NonNull MediaRoute2Provider provider,
+                @Nullable RouteSessionInfo sessionInfo, long requestId) {
             SessionCreationRequest matchingRequest = null;
 
             for (SessionCreationRequest request : mSessionCreationRequests) {
@@ -1071,12 +1131,6 @@
                 return;
             }
 
-            //TODO: remove this when we are sure that request id is properly implemented.
-            if (matchingRequest.mClientRecord.mClientId != toClientId(requestId)) {
-                Slog.w(TAG, "Client id is changed. This shouldn't happen.");
-                return;
-            }
-
             mSessionCreationRequests.remove(matchingRequest);
 
             if (sessionInfo == null) {
@@ -1086,21 +1140,17 @@
                 return;
             }
 
-            RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
-                    .setProviderId(provider.getUniqueId())
-                    .build();
-
             String originalRouteId = matchingRequest.mRoute.getId();
             String originalCategory = matchingRequest.mControlCategory;
             Client2Record client2Record = matchingRequest.mClientRecord;
 
-            if (!sessionInfoWithProviderId.getSelectedRoutes().contains(originalRouteId)
+            if (!sessionInfo.getSelectedRoutes().contains(originalRouteId)
                     || !TextUtils.equals(originalCategory,
-                        sessionInfoWithProviderId.getControlCategory())) {
+                        sessionInfo.getControlCategory())) {
                 Slog.w(TAG, "Created session doesn't match the original request."
                         + " originalRouteId=" + originalRouteId
                         + ", originalCategory=" + originalCategory + ", requestId=" + requestId
-                        + ", sessionInfo=" + sessionInfoWithProviderId);
+                        + ", sessionInfo=" + sessionInfo);
                 notifySessionCreationFailed(matchingRequest.mClientRecord,
                         toClientRequestId(requestId));
                 return;
@@ -1108,12 +1158,12 @@
 
             // Succeeded
             notifySessionCreated(matchingRequest.mClientRecord,
-                    sessionInfoWithProviderId, toClientRequestId(requestId));
-            mSessionToClientMap.put(sessionInfoWithProviderId.getUniqueSessionId(), client2Record);
+                    sessionInfo, toClientRequestId(requestId));
+            mSessionToClientMap.put(sessionInfo.getUniqueSessionId(), client2Record);
             // TODO: Tell managers for the session creation
         }
 
-        private void updateSession(@NonNull MediaRoute2Provider provider,
+        private void onSessionInfoChangedOnHandler(@NonNull MediaRoute2Provider provider,
                 @NonNull RouteSessionInfo sessionInfo) {
             RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
                     .setProviderId(provider.getUniqueId())
@@ -1130,6 +1180,23 @@
             // TODO: Tell managers for the session update
         }
 
+        private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
+                @NonNull RouteSessionInfo sessionInfo) {
+            RouteSessionInfo sessionInfoWithProviderId = new RouteSessionInfo.Builder(sessionInfo)
+                    .setProviderId(provider.getUniqueId())
+                    .build();
+
+            Client2Record client2Record = mSessionToClientMap.get(
+                    sessionInfoWithProviderId.getUniqueSessionId());
+            if (client2Record == null) {
+                Slog.w(TAG, "No matching client found for session=" + sessionInfoWithProviderId);
+                // TODO: Tell managers for the session release
+                return;
+            }
+            notifySessionReleased(client2Record, sessionInfoWithProviderId);
+            // TODO: Tell managers for the session release
+        }
+
         private void notifySessionCreated(Client2Record clientRecord, RouteSessionInfo sessionInfo,
                 int requestId) {
             try {
@@ -1159,6 +1226,16 @@
             }
         }
 
+        private void notifySessionReleased(Client2Record clientRecord,
+                RouteSessionInfo sessionInfo) {
+            try {
+                clientRecord.mClient.notifySessionReleased(sessionInfo);
+            } catch (RemoteException ex) {
+                Slog.w(TAG, "Failed to notify client of the session release."
+                        + " Client probably died.", ex);
+            }
+        }
+
         private void sendControlRequest(MediaRoute2Info route, Intent request) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 3e2bf4e..d77f43b 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -484,6 +484,12 @@
 
     // Binder call
     @Override
+    public void releaseSession(IMediaRouter2Client client, String sessionId) {
+        mService2.releaseSession(client, sessionId);
+    }
+
+    // Binder call
+    @Override
     public void sendControlRequest(IMediaRouter2Client client, MediaRoute2Info route,
             Intent request) {
         mService2.sendControlRequest(client, route, request);
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 4cb41da..ef8f647 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -45,12 +45,12 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.util.Preconditions;
 import com.android.server.ConnectivityService;
 import com.android.server.EventLogTags;
 import com.android.server.connectivity.Vpn;
 
 import java.util.List;
+import java.util.Objects;
 
 /**
  * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
@@ -90,11 +90,11 @@
             @NonNull Handler handler,
             @NonNull Vpn vpn,
             @NonNull VpnProfile profile) {
-        mContext = Preconditions.checkNotNull(context);
-        mConnService = Preconditions.checkNotNull(connService);
-        mHandler = Preconditions.checkNotNull(handler);
-        mVpn = Preconditions.checkNotNull(vpn);
-        mProfile = Preconditions.checkNotNull(profile);
+        mContext = Objects.requireNonNull(context);
+        mConnService = Objects.requireNonNull(connService);
+        mHandler = Objects.requireNonNull(handler);
+        mVpn = Objects.requireNonNull(vpn);
+        mProfile = Objects.requireNonNull(profile);
 
         final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
         mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 99562eb..4a45730 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -91,7 +91,6 @@
 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
 import static com.android.internal.util.ArrayUtils.appendInt;
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -614,12 +613,12 @@
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             INetworkManagementService networkManagement, IPackageManager pm, Clock clock,
             File systemDir, boolean suppressDefaultPolicy) {
-        mContext = checkNotNull(context, "missing context");
-        mActivityManager = checkNotNull(activityManager, "missing activityManager");
-        mNetworkManager = checkNotNull(networkManagement, "missing networkManagement");
+        mContext = Objects.requireNonNull(context, "missing context");
+        mActivityManager = Objects.requireNonNull(activityManager, "missing activityManager");
+        mNetworkManager = Objects.requireNonNull(networkManagement, "missing networkManagement");
         mDeviceIdleController = IDeviceIdleController.Stub.asInterface(ServiceManager.getService(
                 Context.DEVICE_IDLE_CONTROLLER));
-        mClock = checkNotNull(clock, "missing Clock");
+        mClock = Objects.requireNonNull(clock, "missing Clock");
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
         mIPm = pm;
@@ -646,7 +645,7 @@
     }
 
     public void bindConnectivityManager(IConnectivityManager connManager) {
-        mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
+        mConnManager = Objects.requireNonNull(connManager, "missing IConnectivityManager");
     }
 
     @GuardedBy("mUidRulesFirstLock")
@@ -1781,7 +1780,7 @@
             final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
             for (int i = 0; i < matchingSubIds.size(); i++) {
                 final int subId = matchingSubIds.get(i);
-                tm.setPolicyDataEnabled(enabled, subId);
+                tm.createForSubscriptionId(subId).setPolicyDataEnabled(enabled);
             }
         }
     }
@@ -1820,7 +1819,7 @@
 
         final List<String[]> mergedSubscriberIdsList = new ArrayList();
         final SparseArray<String> subIdToSubscriberId = new SparseArray<>(subList.size());
-        for (SubscriptionInfo sub : subList) {
+        for (final SubscriptionInfo sub : subList) {
             final TelephonyManager tmSub = tm.createForSubscriptionId(sub.getSubscriptionId());
             final String subscriberId = tmSub.getSubscriberId();
             if (!TextUtils.isEmpty(subscriberId)) {
@@ -3047,12 +3046,13 @@
         // Verify they're not lying about package name
         mAppOps.checkPackage(callingUid, callingPackage);
 
+        final SubscriptionManager sm;
         final SubscriptionInfo si;
         final PersistableBundle config;
         final long token = Binder.clearCallingIdentity();
         try {
-            si = mContext.getSystemService(SubscriptionManager.class)
-                    .getActiveSubscriptionInfo(subId);
+            sm = mContext.getSystemService(SubscriptionManager.class);
+            si = sm.getActiveSubscriptionInfo(subId);
             config = mCarrierConfigManager.getConfigForSubId(subId);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -3060,7 +3060,7 @@
 
         // First check: is caller the CarrierService?
         if (si != null) {
-            if (si.isEmbedded() && si.canManageSubscription(mContext, callingPackage)) {
+            if (si.isEmbedded() && sm.canManageSubscription(si, callingPackage)) {
                 return;
             }
         }
@@ -3106,17 +3106,16 @@
             return;
         }
 
-        long applicableNetworkTypes = 0;
+        final ArraySet<Integer> applicableNetworkTypes = new ArraySet<Integer>();
         boolean allNetworks = false;
         for (SubscriptionPlan plan : plans) {
             if (plan.getNetworkTypes() == null) {
                 allNetworks = true;
             } else {
-                if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) {
+                final int[] networkTypes = plan.getNetworkTypes();
+                if (!addAll(applicableNetworkTypes, networkTypes)) {
                     throw new IllegalArgumentException(
                             "Multiple subscription plans defined for a single network type.");
-                } else {
-                    applicableNetworkTypes |= plan.getNetworkTypesBitMask();
                 }
             }
         }
@@ -3128,6 +3127,19 @@
         }
     }
 
+    /**
+     * Adds all of the {@code elements} to the {@code set}.
+     *
+     * @return {@code false} if any element is not added because the set already have the value.
+     */
+    private static boolean addAll(@NonNull Set<Integer> set, @NonNull int... elements) {
+        boolean result = true;
+        for (int element : elements) {
+            result &= set.add(element);
+        }
+        return result;
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -3295,7 +3307,7 @@
         enforceSubscriptionPlanValidity(plans);
 
         for (SubscriptionPlan plan : plans) {
-            Preconditions.checkNotNull(plan);
+            Objects.requireNonNull(plan);
         }
 
         final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 6af962b..9eff5d1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -21,8 +21,6 @@
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.text.format.DateUtils.YEAR_IN_MILLIS;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.net.NetworkStats;
 import android.net.NetworkStats.NonMonotonicObserver;
 import android.net.NetworkStatsHistory;
@@ -54,6 +52,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * Logic to record deltas between periodic {@link NetworkStats} snapshots into
@@ -116,9 +115,9 @@
      */
     public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
             DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags) {
-        mRotator = checkNotNull(rotator, "missing FileRotator");
-        mObserver = checkNotNull(observer, "missing NonMonotonicObserver");
-        mDropBox = checkNotNull(dropBox, "missing DropBoxManager");
+        mRotator = Objects.requireNonNull(rotator, "missing FileRotator");
+        mObserver = Objects.requireNonNull(observer, "missing NonMonotonicObserver");
+        mDropBox = Objects.requireNonNull(dropBox, "missing DropBoxManager");
         mCookie = cookie;
 
         mBucketDuration = bucketDuration;
@@ -165,7 +164,7 @@
      * as reference is valid.
      */
     public NetworkStatsCollection getOrLoadCompleteLocked() {
-        checkNotNull(mRotator, "missing FileRotator");
+        Objects.requireNonNull(mRotator, "missing FileRotator");
         NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
         if (res == null) {
             res = loadLocked(Long.MIN_VALUE, Long.MAX_VALUE);
@@ -175,7 +174,7 @@
     }
 
     public NetworkStatsCollection getOrLoadPartialLocked(long start, long end) {
-        checkNotNull(mRotator, "missing FileRotator");
+        Objects.requireNonNull(mRotator, "missing FileRotator");
         NetworkStatsCollection res = mComplete != null ? mComplete.get() : null;
         if (res == null) {
             res = loadLocked(start, end);
@@ -280,7 +279,7 @@
      * {@link #mPersistThresholdBytes}.
      */
     public void maybePersistLocked(long currentTimeMillis) {
-        checkNotNull(mRotator, "missing FileRotator");
+        Objects.requireNonNull(mRotator, "missing FileRotator");
         final long pendingBytes = mPending.getTotalBytes();
         if (pendingBytes >= mPersistThresholdBytes) {
             forcePersistLocked(currentTimeMillis);
@@ -293,7 +292,7 @@
      * Force persisting any pending deltas.
      */
     public void forcePersistLocked(long currentTimeMillis) {
-        checkNotNull(mRotator, "missing FileRotator");
+        Objects.requireNonNull(mRotator, "missing FileRotator");
         if (mPending.isDirty()) {
             if (LOGD) Slog.d(TAG, "forcePersistLocked() writing for " + mCookie);
             try {
@@ -356,7 +355,7 @@
         private final NetworkStatsCollection mCollection;
 
         public CombiningRewriter(NetworkStatsCollection collection) {
-            mCollection = checkNotNull(collection, "missing NetworkStatsCollection");
+            mCollection = Objects.requireNonNull(collection, "missing NetworkStatsCollection");
         }
 
         @Override
@@ -418,7 +417,7 @@
     }
 
     public void importLegacyNetworkLocked(File file) throws IOException {
-        checkNotNull(mRotator, "missing FileRotator");
+        Objects.requireNonNull(mRotator, "missing FileRotator");
 
         // legacy file still exists; start empty to avoid double importing
         mRotator.deleteAll();
@@ -438,7 +437,7 @@
     }
 
     public void importLegacyUidLocked(File file) throws IOException {
-        checkNotNull(mRotator, "missing FileRotator");
+        Objects.requireNonNull(mRotator, "missing FileRotator");
 
         // legacy file still exists; start empty to avoid double importing
         mRotator.deleteAll();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0288f0c..ec8a8e7 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -65,7 +65,6 @@
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
 import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
@@ -148,6 +147,7 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Collect and persist detailed network statistics, and provide this data to
@@ -357,17 +357,18 @@
             TelephonyManager teleManager, NetworkStatsSettings settings,
             NetworkStatsFactory factory, NetworkStatsObservers statsObservers, File systemDir,
             File baseDir) {
-        mContext = checkNotNull(context, "missing Context");
-        mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
-        mAlarmManager = checkNotNull(alarmManager, "missing AlarmManager");
-        mClock = checkNotNull(clock, "missing Clock");
-        mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
-        mTeleManager = checkNotNull(teleManager, "missing TelephonyManager");
-        mWakeLock = checkNotNull(wakeLock, "missing WakeLock");
-        mStatsFactory = checkNotNull(factory, "missing factory");
-        mStatsObservers = checkNotNull(statsObservers, "missing NetworkStatsObservers");
-        mSystemDir = checkNotNull(systemDir, "missing systemDir");
-        mBaseDir = checkNotNull(baseDir, "missing baseDir");
+        mContext = Objects.requireNonNull(context, "missing Context");
+        mNetworkManager = Objects.requireNonNull(networkManager,
+            "missing INetworkManagementService");
+        mAlarmManager = Objects.requireNonNull(alarmManager, "missing AlarmManager");
+        mClock = Objects.requireNonNull(clock, "missing Clock");
+        mSettings = Objects.requireNonNull(settings, "missing NetworkStatsSettings");
+        mTeleManager = Objects.requireNonNull(teleManager, "missing TelephonyManager");
+        mWakeLock = Objects.requireNonNull(wakeLock, "missing WakeLock");
+        mStatsFactory = Objects.requireNonNull(factory, "missing factory");
+        mStatsObservers = Objects.requireNonNull(statsObservers, "missing NetworkStatsObservers");
+        mSystemDir = Objects.requireNonNull(systemDir, "missing systemDir");
+        mBaseDir = Objects.requireNonNull(baseDir, "missing baseDir");
         mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists();
     }
 
@@ -896,11 +897,11 @@
     @Override
     public DataUsageRequest registerUsageCallback(String callingPackage,
                 DataUsageRequest request, Messenger messenger, IBinder binder) {
-        checkNotNull(callingPackage, "calling package is null");
-        checkNotNull(request, "DataUsageRequest is null");
-        checkNotNull(request.template, "NetworkTemplate is null");
-        checkNotNull(messenger, "messenger is null");
-        checkNotNull(binder, "binder is null");
+        Objects.requireNonNull(callingPackage, "calling package is null");
+        Objects.requireNonNull(request, "DataUsageRequest is null");
+        Objects.requireNonNull(request.template, "NetworkTemplate is null");
+        Objects.requireNonNull(messenger, "messenger is null");
+        Objects.requireNonNull(binder, "binder is null");
 
         int callingUid = Binder.getCallingUid();
         @NetworkStatsAccess.Level int accessLevel = checkAccessLevel(callingPackage);
@@ -921,7 +922,7 @@
 
     @Override
     public void unregisterUsageRequest(DataUsageRequest request) {
-        checkNotNull(request, "DataUsageRequest is null");
+        Objects.requireNonNull(request, "DataUsageRequest is null");
 
         int callingUid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
@@ -1795,7 +1796,7 @@
         private final ContentResolver mResolver;
 
         public DefaultNetworkStatsSettings(Context context) {
-            mResolver = checkNotNull(context.getContentResolver());
+            mResolver = Objects.requireNonNull(context.getContentResolver());
             // TODO: adjust these timings for production builds
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 12afef2..a4f4d07 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -236,7 +236,6 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.TriPredicate;
 import com.android.server.DeviceIdleInternal;
@@ -1182,21 +1181,14 @@
 
         @Override
         public void onNotificationBubbleChanged(String key, boolean isBubble) {
-            String pkg;
-            synchronized (mNotificationLock) {
-                NotificationRecord r = mNotificationsByKey.get(key);
-                pkg = r != null && r.sbn != null ? r.sbn.getPackageName() : null;
-            }
-            boolean isAppForeground = pkg != null
-                    && mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
                 if (r != null) {
                     final StatusBarNotification n = r.sbn;
                     final int callingUid = n.getUid();
-                    pkg = n.getPackageName();
+                    final String pkg = n.getPackageName();
                     if (isBubble && isNotificationAppropriateToBubble(r, pkg, callingUid,
-                            null /* oldEntry */, isAppForeground)) {
+                            null /* oldEntry */)) {
                         r.getNotification().flags |= FLAG_BUBBLE;
                     } else {
                         r.getNotification().flags &= ~FLAG_BUBBLE;
@@ -2261,8 +2253,8 @@
 
     private void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
             boolean fromApp, boolean fromListener) {
-        Preconditions.checkNotNull(group);
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(group);
+        Objects.requireNonNull(pkg);
 
         final NotificationChannelGroup preUpdate =
                 mPreferencesHelper.getNotificationChannelGroup(group.getId(), pkg, uid);
@@ -3005,7 +2997,7 @@
             boolean needsPolicyFileChange = false;
             for (int i = 0; i < channelsSize; i++) {
                 final NotificationChannel channel = channels.get(i);
-                Preconditions.checkNotNull(channel, "channel in list is null");
+                Objects.requireNonNull(channel, "channel in list is null");
                 needsPolicyFileChange = mPreferencesHelper.createNotificationChannel(pkg, uid,
                         channel, true /* fromTargetApp */,
                         mConditionProviders.isPackageOrComponentAllowed(
@@ -3126,7 +3118,7 @@
         public void updateNotificationChannelForPackage(String pkg, int uid,
                 NotificationChannel channel) {
             enforceSystemOrSystemUI("Caller not system or systemui");
-            Preconditions.checkNotNull(channel);
+            Objects.requireNonNull(channel);
             updateNotificationChannelInt(pkg, uid, channel, false);
         }
 
@@ -3877,21 +3869,21 @@
 
         @Override
         public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
-            Preconditions.checkNotNull(id, "Id is null");
+            Objects.requireNonNull(id, "Id is null");
             enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
             return mZenModeHelper.getAutomaticZenRule(id);
         }
 
         @Override
         public String addAutomaticZenRule(AutomaticZenRule automaticZenRule) {
-            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
-            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+            Objects.requireNonNull(automaticZenRule, "automaticZenRule is null");
+            Objects.requireNonNull(automaticZenRule.getName(), "Name is null");
             if (automaticZenRule.getOwner() == null
                     && automaticZenRule.getConfigurationActivity() == null) {
                 throw new NullPointerException(
                         "Rule must have a conditionproviderservice and/or configuration activity");
             }
-            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+            Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
             if (automaticZenRule.getZenPolicy() != null
                     && automaticZenRule.getInterruptionFilter() != INTERRUPTION_FILTER_PRIORITY) {
                 throw new IllegalArgumentException("ZenPolicy is only applicable to "
@@ -3906,14 +3898,14 @@
         @Override
         public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule)
                 throws RemoteException {
-            Preconditions.checkNotNull(automaticZenRule, "automaticZenRule is null");
-            Preconditions.checkNotNull(automaticZenRule.getName(), "Name is null");
+            Objects.requireNonNull(automaticZenRule, "automaticZenRule is null");
+            Objects.requireNonNull(automaticZenRule.getName(), "Name is null");
             if (automaticZenRule.getOwner() == null
                     && automaticZenRule.getConfigurationActivity() == null) {
                 throw new NullPointerException(
                         "Rule must have a conditionproviderservice and/or configuration activity");
             }
-            Preconditions.checkNotNull(automaticZenRule.getConditionId(), "ConditionId is null");
+            Objects.requireNonNull(automaticZenRule.getConditionId(), "ConditionId is null");
             enforcePolicyAccess(Binder.getCallingUid(), "updateAutomaticZenRule");
 
             return mZenModeHelper.updateAutomaticZenRule(id, automaticZenRule,
@@ -3922,7 +3914,7 @@
 
         @Override
         public boolean removeAutomaticZenRule(String id) throws RemoteException {
-            Preconditions.checkNotNull(id, "Id is null");
+            Objects.requireNonNull(id, "Id is null");
             // Verify that they can modify zen rules.
             enforcePolicyAccess(Binder.getCallingUid(), "removeAutomaticZenRule");
 
@@ -3931,7 +3923,7 @@
 
         @Override
         public boolean removeAutomaticZenRules(String packageName) throws RemoteException {
-            Preconditions.checkNotNull(packageName, "Package name is null");
+            Objects.requireNonNull(packageName, "Package name is null");
             enforceSystemOrSystemUI("removeAutomaticZenRules");
 
             return mZenModeHelper.removeAutomaticZenRules(packageName, "removeAutomaticZenRules");
@@ -3939,7 +3931,7 @@
 
         @Override
         public int getRuleInstanceCount(ComponentName owner) throws RemoteException {
-            Preconditions.checkNotNull(owner, "Owner is null");
+            Objects.requireNonNull(owner, "Owner is null");
             enforceSystemOrSystemUI("getRuleInstanceCount");
 
             return mZenModeHelper.getCurrentInstanceCount(owner);
@@ -3947,8 +3939,8 @@
 
         @Override
         public void setAutomaticZenRuleState(String id, Condition condition) {
-            Preconditions.checkNotNull(id, "id is null");
-            Preconditions.checkNotNull(condition, "Condition is null");
+            Objects.requireNonNull(id, "id is null");
+            Objects.requireNonNull(condition, "Condition is null");
 
             enforcePolicyAccess(Binder.getCallingUid(), "setAutomaticZenRuleState");
 
@@ -4292,7 +4284,7 @@
 
         @Override
         public boolean isNotificationListenerAccessGranted(ComponentName listener) {
-            Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
             checkCallerIsSystemOrSameApp(listener.getPackageName());
             return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
                     getCallingUserHandle().getIdentifier());
@@ -4301,7 +4293,7 @@
         @Override
         public boolean isNotificationListenerAccessGrantedForUser(ComponentName listener,
                 int userId) {
-            Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
             checkCallerIsSystem();
             return mListeners.isPackageOrComponentAllowed(listener.flattenToString(),
                     userId);
@@ -4309,7 +4301,7 @@
 
         @Override
         public boolean isNotificationAssistantAccessGranted(ComponentName assistant) {
-            Preconditions.checkNotNull(assistant);
+            Objects.requireNonNull(assistant);
             checkCallerIsSystemOrSameApp(assistant.getPackageName());
             return mAssistants.isPackageOrComponentAllowed(assistant.flattenToString(),
                     getCallingUserHandle().getIdentifier());
@@ -4332,7 +4324,7 @@
         @Override
         public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId,
                 boolean granted) {
-            Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
             checkCallerIsSystemOrShell();
             final long identity = Binder.clearCallingIdentity();
             try {
@@ -4450,7 +4442,7 @@
         public void updateNotificationChannelGroupFromPrivilegedListener(
                 INotificationListener token, String pkg, UserHandle user,
                 NotificationChannelGroup group) throws RemoteException {
-            Preconditions.checkNotNull(user);
+            Objects.requireNonNull(user);
             verifyPrivilegedListener(token, user, false);
             createNotificationChannelGroup(
                     pkg, getUidForPackageAndUser(pkg, user), group, false, true);
@@ -4460,9 +4452,9 @@
         @Override
         public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
                 String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
-            Preconditions.checkNotNull(channel);
-            Preconditions.checkNotNull(pkg);
-            Preconditions.checkNotNull(user);
+            Objects.requireNonNull(channel);
+            Objects.requireNonNull(pkg);
+            Objects.requireNonNull(user);
 
             verifyPrivilegedListener(token, user, false);
             updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
@@ -4471,8 +4463,8 @@
         @Override
         public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
                 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
-            Preconditions.checkNotNull(pkg);
-            Preconditions.checkNotNull(user);
+            Objects.requireNonNull(pkg);
+            Objects.requireNonNull(user);
             verifyPrivilegedListener(token, user, true);
 
             return mPreferencesHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
@@ -4483,8 +4475,8 @@
         public ParceledListSlice<NotificationChannelGroup>
                 getNotificationChannelGroupsFromPrivilegedListener(
                 INotificationListener token, String pkg, UserHandle user) throws RemoteException {
-            Preconditions.checkNotNull(pkg);
-            Preconditions.checkNotNull(user);
+            Objects.requireNonNull(pkg);
+            Objects.requireNonNull(user);
             verifyPrivilegedListener(token, user, true);
 
             List<NotificationChannelGroup> groups = new ArrayList<>();
@@ -4520,7 +4512,7 @@
 
         @Override
         public boolean isPackagePaused(String pkg) {
-            Preconditions.checkNotNull(pkg);
+            Objects.requireNonNull(pkg);
             checkCallerIsSameApp(pkg);
 
             return isPackagePausedOrSuspended(pkg, Binder.getCallingUid());
@@ -5386,7 +5378,7 @@
     private void flagNotificationForBubbles(NotificationRecord r, String pkg, int userId,
             NotificationRecord oldRecord, boolean isAppForeground) {
         Notification notification = r.getNotification();
-        if (isNotificationAppropriateToBubble(r, pkg, userId, oldRecord, isAppForeground)) {
+        if (isNotificationAppropriateToBubble(r, pkg, userId, oldRecord)) {
             notification.flags |= FLAG_BUBBLE;
         } else {
             notification.flags &= ~FLAG_BUBBLE;
@@ -5406,7 +5398,7 @@
      * accounting for user choice & policy.
      */
     private boolean isNotificationAppropriateToBubble(NotificationRecord r, String pkg, int userId,
-            NotificationRecord oldRecord, boolean isAppForeground) {
+            NotificationRecord oldRecord) {
         Notification notification = r.getNotification();
         if (!canBubble(r, pkg, userId)) {
             // no log: canBubble has its own
@@ -5418,11 +5410,6 @@
             return false;
         }
 
-        if (isAppForeground) {
-            // If the app is foreground it always gets to bubble
-            return true;
-        }
-
         if (oldRecord != null && (oldRecord.getNotification().flags & FLAG_BUBBLE) != 0) {
             // This is an update to an active bubble
             return true;
@@ -5438,7 +5425,7 @@
         boolean isMessageStyle = Notification.MessagingStyle.class.equals(
                 notification.getNotificationStyle());
         if (!isMessageStyle && (peopleList == null || peopleList.isEmpty())) {
-            logBubbleError(r.getKey(), "if not foreground, must have a person and be "
+            logBubbleError(r.getKey(), "Must have a person and be "
                     + "Notification.MessageStyle or Notification.CATEGORY_CALL");
             return false;
         }
@@ -5446,6 +5433,11 @@
         // Communication is a message or a call
         boolean isCall = CATEGORY_CALL.equals(notification.category);
         boolean hasForegroundService = (notification.flags & FLAG_FOREGROUND_SERVICE) != 0;
+        if (hasForegroundService && !isCall) {
+            logBubbleError(r.getKey(),
+                    "foreground services must be Notification.CATEGORY_CALL to bubble");
+            return false;
+        }
         if (isMessageStyle) {
             if (hasValidRemoteInput(notification)) {
                 return true;
@@ -5459,7 +5451,7 @@
             logBubbleError(r.getKey(), "calls require foreground service");
             return false;
         }
-        logBubbleError(r.getKey(), "if not foreground, must be "
+        logBubbleError(r.getKey(), "Must be "
                 + "Notification.MessageStyle or Notification.CATEGORY_CALL");
         return false;
     }
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index b1ffa8c..cdb0a17 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -621,10 +621,10 @@
     @Override
     public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
             boolean fromTargetApp) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(group);
-        Preconditions.checkNotNull(group.getId());
-        Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
+        Objects.requireNonNull(pkg);
+        Objects.requireNonNull(group);
+        Objects.requireNonNull(group.getId());
+        Objects.requireNonNull(!TextUtils.isEmpty(group.getName()));
         synchronized (mPackagePreferences) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -658,9 +658,9 @@
     @Override
     public boolean createNotificationChannel(String pkg, int uid, NotificationChannel channel,
             boolean fromTargetApp, boolean hasDndAccess) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(channel);
-        Preconditions.checkNotNull(channel.getId());
+        Objects.requireNonNull(pkg);
+        Objects.requireNonNull(channel);
+        Objects.requireNonNull(channel.getId());
         Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
         boolean needsPolicyFileChange = false;
         synchronized (mPackagePreferences) {
@@ -788,8 +788,8 @@
     @Override
     public void updateNotificationChannel(String pkg, int uid, NotificationChannel updatedChannel,
             boolean fromUser) {
-        Preconditions.checkNotNull(updatedChannel);
-        Preconditions.checkNotNull(updatedChannel.getId());
+        Objects.requireNonNull(updatedChannel);
+        Objects.requireNonNull(updatedChannel.getId());
         synchronized (mPackagePreferences) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -850,7 +850,7 @@
     @Override
     public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
             boolean includeDeleted) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         synchronized (mPackagePreferences) {
             PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -891,8 +891,8 @@
     @Override
     @VisibleForTesting
     public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(channelId);
+        Objects.requireNonNull(pkg);
+        Objects.requireNonNull(channelId);
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -904,7 +904,7 @@
 
     @Override
     public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -993,7 +993,7 @@
 
     public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
             int uid, String groupId, boolean includeDeleted) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
@@ -1016,7 +1016,7 @@
 
     public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
             int uid) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
             if (r == null) {
@@ -1029,7 +1029,7 @@
     @Override
     public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
             int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
@@ -1111,7 +1111,7 @@
     @Override
     public ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid,
             boolean includeDeleted) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         List<NotificationChannel> channels = new ArrayList<>();
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
@@ -1168,7 +1168,7 @@
     }
 
     public int getDeletedChannelCount(String pkg, int uid) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         int deletedCount = 0;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
@@ -1187,7 +1187,7 @@
     }
 
     public int getBlockedChannelCount(String pkg, int uid) {
-        Preconditions.checkNotNull(pkg);
+        Objects.requireNonNull(pkg);
         int blockedCount = 0;
         synchronized (mPackagePreferences) {
             PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1947ac..b782ca96 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -1154,6 +1154,10 @@
                 throws RemoteException, IOException {
             PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, 0,
                     userId);
+            if (packageInfo == null) {
+                throw new IOException("Unable to get target package");
+            }
+
             String baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
 
             ApkAssets apkAssets = null;
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index 9a1b30d..1e2d52d 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -38,6 +38,7 @@
 import com.android.server.SystemConfig;
 
 import java.io.FileDescriptor;
+import java.util.Objects;
 
 /**
  * Implementation of the service that provides a privileged API to capture and consume bugreports.
@@ -67,9 +68,9 @@
             FileDescriptor bugreportFd, FileDescriptor screenshotFd,
             int bugreportMode, IDumpstateListener listener) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
-        Preconditions.checkNotNull(callingPackage);
-        Preconditions.checkNotNull(bugreportFd);
-        Preconditions.checkNotNull(listener);
+        Objects.requireNonNull(callingPackage);
+        Objects.requireNonNull(bugreportFd);
+        Objects.requireNonNull(listener);
         validateBugreportMode(bugreportMode);
         final long identity = Binder.clearCallingIdentity();
         try {
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/services/core/java/com/android/server/people/PeopleServiceInternal.java
similarity index 72%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to services/core/java/com/android/server/people/PeopleServiceInternal.java
index 1fefda4..31d30362 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package com.android.server.people;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+import android.service.appprediction.IPredictionService;
+
+/**
+ * @hide Only for use within the system server.
+ */
+public abstract class PeopleServiceInternal extends IPredictionService.Stub {}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 5ae8c58..307a07b 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -502,6 +502,9 @@
             } catch (RemoteException re) {
                 Slog.e(TAG, "Unable to contact apexservice", re);
                 return false;
+            } catch (Exception e) {
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index d2a6b42..b25e1e2 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -48,6 +48,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub {
     private static final String TAG = "CrossProfileAppsService";
@@ -67,7 +68,7 @@
 
     @Override
     public List<UserHandle> getTargetUserProfiles(String callingPackage) {
-        Preconditions.checkNotNull(callingPackage);
+        Objects.requireNonNull(callingPackage);
 
         verifyCallingPackage(callingPackage);
 
@@ -87,8 +88,8 @@
             ComponentName component,
             @UserIdInt int userId,
             boolean launchMainActivity) throws RemoteException {
-        Preconditions.checkNotNull(callingPackage);
-        Preconditions.checkNotNull(component);
+        Objects.requireNonNull(callingPackage);
+        Objects.requireNonNull(component);
 
         verifyCallingPackage(callingPackage);
 
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 70c0f8d..f9cfee1 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -38,6 +38,7 @@
 import java.io.PrintWriter;
 import java.security.PublicKey;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /*
@@ -219,10 +220,10 @@
     }
 
     public void addScannedPackageLPw(AndroidPackage pkg) {
-        Preconditions.checkNotNull(pkg, "Attempted to add null pkg to ksms.");
-        Preconditions.checkNotNull(pkg.getPackageName(), "Attempted to add null pkg to ksms.");
+        Objects.requireNonNull(pkg, "Attempted to add null pkg to ksms.");
+        Objects.requireNonNull(pkg.getPackageName(), "Attempted to add null pkg to ksms.");
         PackageSetting ps = mPackages.get(pkg.getPackageName());
-        Preconditions.checkNotNull(ps, "pkg: " + pkg.getPackageName()
+        Objects.requireNonNull(ps, "pkg: " + pkg.getPackageName()
                     + "does not have a corresponding entry in mPackages.");
         addSigningKeySetToPackageLPw(ps, pkg.getSigningDetails().publicKeys);
         if (pkg.getKeySetMapping() != null) {
@@ -504,7 +505,7 @@
      * Adds the given PublicKey to the system, deduping as it goes.
      */
     private long addPublicKeyLPw(PublicKey key) {
-        Preconditions.checkNotNull(key, "Cannot add null public key!");
+        Objects.requireNonNull(key, "Cannot add null public key!");
         long id = getIdForPublicKeyLPr(key);
         if (id != PUBLIC_KEY_NOT_FOUND) {
 
@@ -574,7 +575,7 @@
 
         /* remove refs from common keysets and public keys */
         PackageSetting pkg = mPackages.get(packageName);
-        Preconditions.checkNotNull(pkg, "pkg name: " + packageName
+        Objects.requireNonNull(pkg, "pkg name: " + packageName
                 + "does not have a corresponding entry in mPackages.");
         long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
         decrementKeySetLPw(signingKeySetId);
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 673e265..c4ef856 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -78,6 +78,7 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Service that manages requests and callbacks for launchers that support
@@ -136,15 +137,15 @@
         public LauncherAppsImpl(Context context) {
             mContext = context;
             mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            mUserManagerInternal = Preconditions.checkNotNull(
+            mUserManagerInternal = Objects.requireNonNull(
                     LocalServices.getService(UserManagerInternal.class));
-            mUsageStatsManagerInternal = Preconditions.checkNotNull(
+            mUsageStatsManagerInternal = Objects.requireNonNull(
                     LocalServices.getService(UsageStatsManagerInternal.class));
-            mActivityManagerInternal = Preconditions.checkNotNull(
+            mActivityManagerInternal = Objects.requireNonNull(
                     LocalServices.getService(ActivityManagerInternal.class));
-            mActivityTaskManagerInternal = Preconditions.checkNotNull(
+            mActivityTaskManagerInternal = Objects.requireNonNull(
                     LocalServices.getService(ActivityTaskManagerInternal.class));
-            mShortcutServiceInternal = Preconditions.checkNotNull(
+            mShortcutServiceInternal = Objects.requireNonNull(
                     LocalServices.getService(ShortcutServiceInternal.class));
             mShortcutServiceInternal.addListener(mPackageMonitor);
             mCallbackHandler = BackgroundThread.getHandler();
@@ -558,7 +559,7 @@
             if (!canAccessProfile(user.getIdentifier(), "Cannot check package")) {
                 return null;
             }
-            Preconditions.checkNotNull(component);
+            Objects.requireNonNull(component);
 
             // All right, create the sender.
             Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(component);
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ac183dc..426cd01 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -119,7 +119,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.DexManager;
@@ -140,6 +139,7 @@
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
@@ -511,7 +511,7 @@
         this.userId = userId;
         mOriginalInstallerUid = installerUid;
         mInstallerUid = installerUid;
-        mInstallSource = Preconditions.checkNotNull(installSource);
+        mInstallSource = Objects.requireNonNull(installSource);
         this.params = params;
         this.createdMillis = createdMillis;
         this.updatedMillis = createdMillis;
@@ -1143,7 +1143,7 @@
      * permissions.
      */
     private boolean markAsCommitted(@NonNull IntentSender statusReceiver) {
-        Preconditions.checkNotNull(statusReceiver);
+        Objects.requireNonNull(statusReceiver);
 
         List<PackageInstallerSession> childSessions = getChildSessions();
 
@@ -1408,8 +1408,8 @@
 
     @Override
     public void transfer(String packageName, IntentSender statusReceiver) {
-        Preconditions.checkNotNull(statusReceiver);
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+        Objects.requireNonNull(packageName);
 
         try {
             assertCanBeTransferredAndReturnNewOwner(packageName);
@@ -1607,9 +1607,9 @@
             localObserver = null;
         } else {
             if (!params.isMultiPackage) {
-                Preconditions.checkNotNull(mPackageName);
-                Preconditions.checkNotNull(mSigningDetails);
-                Preconditions.checkNotNull(mResolvedBaseFile);
+                Objects.requireNonNull(mPackageName);
+                Objects.requireNonNull(mSigningDetails);
+                Objects.requireNonNull(mResolvedBaseFile);
 
                 if (needToAskForPermissionsLocked()) {
                     // User needs to confirm installation;
@@ -1683,7 +1683,7 @@
                 computeProgressLocked(true);
 
                 // Unpack native libraries for non-incremental installation
-                if (isIncrementalInstallation()) {
+                if (!isIncrementalInstallation()) {
                     extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 785ca7d..6bd9c48 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11319,6 +11319,12 @@
                             "Static shared libs cannot declare permission groups");
                 }
 
+                // Static shared libs cannot declare features
+                if (pkg.getFeatures() != null && !pkg.getFeatures().isEmpty()) {
+                    throw new PackageManagerException(
+                            "Static shared libs cannot declare features");
+                }
+
                 // Static shared libs cannot declare permissions
                 if (pkg.getPermissions() != null && !pkg.getPermissions().isEmpty()) {
                     throw new PackageManagerException(
@@ -12872,7 +12878,7 @@
 
     @Override
     public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
-        Preconditions.checkNotNull("packageNames cannot be null", packageNames);
+        Preconditions.checkNotNull(packageNames, "packageNames cannot be null");
         mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                 "getUnsuspendablePackagesForUser");
         final int callingUid = Binder.getCallingUid();
@@ -13446,9 +13452,7 @@
 
             // Okay!
             targetPackageSetting.setInstallerPackageName(installerPackageName);
-            if (installerPackageName != null) {
-                mSettings.mInstallerPackages.add(installerPackageName);
-            }
+            mSettings.addInstallerPackageNames(targetPackageSetting.installSource);
             scheduleWriteSettingsLocked();
         }
     }
@@ -15154,7 +15158,8 @@
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
 
         final String pkgName = pkg.getPackageName();
-        final String installerPackageName = installArgs.installSource.installerPackageName;
+        final InstallSource installSource = installArgs.installSource;
+        final String installerPackageName = installSource.installerPackageName;
         final int[] installedForUsers = res.origUsers;
         final int installReason = installArgs.installReason;
 
@@ -15165,7 +15170,7 @@
             // For system-bundled packages, we assume that installing an upgraded version
             // of the package implies that the user actually wants to run that new code,
             // so we enable the package.
-            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            final PackageSetting ps = mSettings.mPackages.get(pkgName);
             final int userId = installArgs.user.getIdentifier();
             if (ps != null) {
                 if (isSystemApp(pkg)) {
@@ -15202,8 +15207,8 @@
                     ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
                 }
 
-                ps.setInstallSource(installArgs.installSource);
-
+                ps.setInstallSource(installSource);
+                mSettings.addInstallerPackageNames(installSource);
 
                 // When replacing an existing package, preserve the original install reason for all
                 // users that had the package installed before.
@@ -15233,7 +15238,6 @@
             res.name = pkgName;
             res.uid = pkg.getUid();
             res.pkg = pkg;
-            mSettings.setInstallerPackageName(pkgName, installerPackageName);
             res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
             //to update install status
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
@@ -23051,6 +23055,11 @@
         }
 
         @Override
+        public void setDeviceOwnerProtectedPackages(List<String> packageNames) {
+            mProtectedPackages.setDeviceOwnerProtectedPackages(packageNames);
+        }
+
+        @Override
         public boolean isPackageDataProtected(int userId, String packageName) {
             return mProtectedPackages.isPackageDataProtected(userId, packageName);
         }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 671450d..0c0b93b 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -41,6 +41,7 @@
 import java.io.File;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -172,7 +173,7 @@
     }
 
     public void setInstallSource(InstallSource installSource) {
-        this.installSource = Preconditions.checkNotNull(installSource);
+        this.installSource = Objects.requireNonNull(installSource);
     }
 
     void removeInstallerPackage(String packageName) {
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index 231168e..4da3cc3 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -24,6 +24,10 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Manages package names that need special protection.
@@ -49,6 +53,10 @@
     @GuardedBy("this")
     private final String mDeviceProvisioningPackage;
 
+    @Nullable
+    @GuardedBy("this")
+    private List<String> mDeviceOwnerProtectedPackages;
+
     private final Context mContext;
 
     public ProtectedPackages(Context context) {
@@ -70,6 +78,10 @@
                 : profileOwnerPackages.clone();
     }
 
+    public synchronized void setDeviceOwnerProtectedPackages(List<String> packageNames) {
+        mDeviceOwnerProtectedPackages = new ArrayList<String>(packageNames);
+    }
+
     private synchronized boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
         if (packageName == null) {
             return false;
@@ -105,7 +117,8 @@
      * can modify its data or package state.
      */
     private synchronized boolean isProtectedPackage(String packageName) {
-        return packageName != null && packageName.equals(mDeviceProvisioningPackage);
+        return packageName != null && (packageName.equals(mDeviceProvisioningPackage)
+                || ArrayUtils.contains(mDeviceOwnerProtectedPackages, packageName));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9642a1a..f9a3361 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -280,8 +280,11 @@
     /** Map from package name to settings */
     final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
 
-    /** List of packages that installed other packages */
-    final ArraySet<String> mInstallerPackages = new ArraySet<>();
+    /**
+     * List of packages that were involved in installing other packages, i.e. are listed
+     * in at least one app's InstallSource.
+     */
+    private final ArraySet<String> mInstallerPackages = new ArraySet<>();
 
     /** Map from package name to appId and excluded userids */
     private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
@@ -441,16 +444,6 @@
         return mPermissions.canPropagatePermissionToInstantApp(permName);
     }
 
-    void setInstallerPackageName(String pkgName, String installerPkgName) {
-        PackageSetting p = mPackages.get(pkgName);
-        if (p != null) {
-            p.setInstallerPackageName(installerPkgName);
-            if (installerPkgName != null) {
-                mInstallerPackages.add(installerPkgName);
-            }
-        }
-    }
-
     /** Gets and optionally creates a new shared user id. */
     SharedUserSetting getSharedUserLPw(String name, int pkgFlags, int pkgPrivateFlags,
             boolean create) throws PackageManagerException {
@@ -3777,9 +3770,10 @@
         }
         if (packageSetting != null) {
             packageSetting.uidError = "true".equals(uidError);
-            packageSetting.installSource = InstallSource.create(
+            InstallSource installSource = InstallSource.create(
                     installInitiatingPackageName, installOriginatingPackageName,
                     installerPackageName, "true".equals(isOrphaned));
+            packageSetting.installSource = installSource;
             packageSetting.volumeUuid = volumeUuid;
             packageSetting.categoryHint = categoryHint;
             packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
@@ -3809,9 +3803,7 @@
                 packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
             }
 
-            if (installerPackageName != null) {
-                mInstallerPackages.add(installerPackageName);
-            }
+            addInstallerPackageNames(installSource);
 
             int outerDepth = parser.getDepth();
             int type;
@@ -3870,6 +3862,18 @@
         }
     }
 
+    void addInstallerPackageNames(InstallSource installSource) {
+        if (installSource.installerPackageName != null) {
+            mInstallerPackages.add(installSource.installerPackageName);
+        }
+        if (installSource.initiatingPackageName != null) {
+            mInstallerPackages.add(installSource.initiatingPackageName);
+        }
+        if (installSource.originatingPackageName != null) {
+            mInstallerPackages.add(installSource.originatingPackageName);
+        }
+    }
+
     private void readDisabledComponentsLPw(PackageSettingBase packageSetting, XmlPullParser parser,
             int userId) throws IOException, XmlPullParserException {
         int outerDepth = parser.getDepth();
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index 815f885..dc534a7 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -38,6 +38,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Deque;
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingDeque;
@@ -157,7 +158,7 @@
     public void saveBitmapLocked(ShortcutInfo shortcut,
             int maxDimension, CompressFormat format, int quality) {
         final Icon icon = shortcut.getIcon();
-        Preconditions.checkNotNull(icon);
+        Objects.requireNonNull(icon);
 
         final Bitmap original = icon.getBitmap();
         if (original == null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 06c71ba..0274aee 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -55,6 +55,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
 
@@ -454,7 +455,7 @@
 
     public void updateInvisibleShortcutForPinRequestWith(@NonNull ShortcutInfo shortcut) {
         final ShortcutInfo source = mShortcuts.get(shortcut.getId());
-        Preconditions.checkNotNull(source);
+        Objects.requireNonNull(source);
 
         mShortcutUser.mService.validateShortcutForPinRequest(shortcut);
 
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 0629d9e..6d9d69e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -28,6 +28,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
+import java.util.Objects;
 
 /**
  * All methods should be guarded by {@code #mShortcutUser.mService.mLock}.
@@ -49,7 +50,7 @@
         mShortcutUser = shortcutUser;
         mPackageUserId = packageUserId;
         mPackageName = Preconditions.checkStringNotEmpty(packageName);
-        mPackageInfo = Preconditions.checkNotNull(packageInfo);
+        mPackageInfo = Objects.requireNonNull(packageInfo);
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index f0a1c70..261418c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -429,17 +429,17 @@
 
     @VisibleForTesting
     ShortcutService(Context context, Looper looper, boolean onlyForPackageManagerApis) {
-        mContext = Preconditions.checkNotNull(context);
+        mContext = Objects.requireNonNull(context);
         LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
         mHandler = new Handler(looper);
         mIPackageManager = AppGlobals.getPackageManager();
-        mPackageManagerInternal = Preconditions.checkNotNull(
+        mPackageManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(PackageManagerInternal.class));
-        mUserManagerInternal = Preconditions.checkNotNull(
+        mUserManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(UserManagerInternal.class));
-        mUsageStatsManagerInternal = Preconditions.checkNotNull(
+        mUsageStatsManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(UsageStatsManagerInternal.class));
-        mActivityManagerInternal = Preconditions.checkNotNull(
+        mActivityManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(ActivityManagerInternal.class));
 
         mShortcutRequestPinProcessor = new ShortcutRequestPinProcessor(this, mLock);
@@ -1680,7 +1680,7 @@
                     "Re-publishing ShortcutInfo returned by server is not supported."
                     + " Some information such as icon may lost from shortcut.");
         }
-        Preconditions.checkNotNull(shortcut, "Null shortcut detected");
+        Objects.requireNonNull(shortcut, "Null shortcut detected");
         if (shortcut.getActivity() != null) {
             Preconditions.checkState(
                     shortcut.getPackage().equals(shortcut.getActivity().getPackageName()),
@@ -1947,7 +1947,7 @@
     @Override
     public boolean requestPinShortcut(String packageName, ShortcutInfo shortcut,
             IntentSender resultIntent, int userId) {
-        Preconditions.checkNotNull(shortcut);
+        Objects.requireNonNull(shortcut);
         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
         return requestPinItem(packageName, userId, shortcut, null, null, resultIntent);
     }
@@ -1955,7 +1955,7 @@
     @Override
     public Intent createShortcutResultIntent(String packageName, ShortcutInfo shortcut, int userId)
             throws RemoteException {
-        Preconditions.checkNotNull(shortcut);
+        Objects.requireNonNull(shortcut);
         Preconditions.checkArgument(shortcut.isEnabled(), "Shortcut must be enabled");
         verifyCaller(packageName, userId);
         verifyShortcutInfoPackage(packageName, shortcut);
@@ -2019,7 +2019,7 @@
     public void disableShortcuts(String packageName, List shortcutIds,
             CharSequence disabledMessage, int disabledMessageResId, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-        Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+        Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
 
         synchronized (mLock) {
             throwIfUserLockedL(userId);
@@ -2054,7 +2054,7 @@
     @Override
     public void enableShortcuts(String packageName, List shortcutIds, @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-        Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+        Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
 
         synchronized (mLock) {
             throwIfUserLockedL(userId);
@@ -2081,7 +2081,7 @@
     public void removeDynamicShortcuts(String packageName, List shortcutIds,
             @UserIdInt int userId) {
         verifyCaller(packageName, userId);
-        Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+        Objects.requireNonNull(shortcutIds, "shortcutIds must be provided");
 
         synchronized (mLock) {
             throwIfUserLockedL(userId);
@@ -2256,7 +2256,7 @@
     public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
         verifyCaller(packageName, userId);
 
-        Preconditions.checkNotNull(shortcutId);
+        Objects.requireNonNull(shortcutId);
 
         if (DEBUG) {
             Slog.d(TAG, String.format("reportShortcutUsed: Shortcut %s package %s used on user %d",
@@ -2713,7 +2713,7 @@
                 @NonNull List<String> shortcutIds, int userId) {
             // Calling permission must be checked by LauncherAppsImpl.
             Preconditions.checkStringNotEmpty(packageName, "packageName");
-            Preconditions.checkNotNull(shortcutIds, "shortcutIds");
+            Objects.requireNonNull(shortcutIds, "shortcutIds");
 
             synchronized (mLock) {
                 throwIfUserLockedL(userId);
@@ -2766,16 +2766,16 @@
         @Override
         public void addListener(@NonNull ShortcutChangeListener listener) {
             synchronized (mLock) {
-                mListeners.add(Preconditions.checkNotNull(listener));
+                mListeners.add(Objects.requireNonNull(listener));
             }
         }
 
         @Override
         public int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
-            Preconditions.checkNotNull(callingPackage, "callingPackage");
-            Preconditions.checkNotNull(packageName, "packageName");
-            Preconditions.checkNotNull(shortcutId, "shortcutId");
+            Objects.requireNonNull(callingPackage, "callingPackage");
+            Objects.requireNonNull(packageName, "packageName");
+            Objects.requireNonNull(shortcutId, "shortcutId");
 
             synchronized (mLock) {
                 throwIfUserLockedL(userId);
@@ -2800,9 +2800,9 @@
         public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
                 @NonNull String callingPackage, @NonNull String packageName,
                 @NonNull String shortcutId, int userId) {
-            Preconditions.checkNotNull(callingPackage, "callingPackage");
-            Preconditions.checkNotNull(packageName, "packageName");
-            Preconditions.checkNotNull(shortcutId, "shortcutId");
+            Objects.requireNonNull(callingPackage, "callingPackage");
+            Objects.requireNonNull(packageName, "packageName");
+            Objects.requireNonNull(shortcutId, "shortcutId");
 
             synchronized (mLock) {
                 throwIfUserLockedL(userId);
@@ -2854,7 +2854,7 @@
         public boolean requestPinAppWidget(@NonNull String callingPackage,
                 @NonNull AppWidgetProviderInfo appWidget, @Nullable Bundle extras,
                 @Nullable IntentSender resultIntent, int userId) {
-            Preconditions.checkNotNull(appWidget);
+            Objects.requireNonNull(appWidget);
             return requestPinItem(callingPackage, userId, null, appWidget, extras, resultIntent);
         }
 
@@ -2865,7 +2865,7 @@
 
         @Override
         public boolean isForegroundDefaultLauncher(@NonNull String callingPackage, int callingUid) {
-            Preconditions.checkNotNull(callingPackage);
+            Objects.requireNonNull(callingPackage);
 
             final int userId = UserHandle.getUserId(callingUid);
             final ComponentName defaultLauncher = getDefaultLauncher(userId);
@@ -3411,7 +3411,7 @@
     List<ResolveInfo> queryActivities(@NonNull Intent baseIntent,
             @NonNull String packageName, @Nullable ComponentName activity, int userId) {
 
-        baseIntent.setPackage(Preconditions.checkNotNull(packageName));
+        baseIntent.setPackage(Objects.requireNonNull(packageName));
         if (activity != null) {
             baseIntent.setComponent(activity);
         }
@@ -3529,7 +3529,7 @@
     @Nullable
     ComponentName injectGetPinConfirmationActivity(@NonNull String launcherPackageName,
             int launcherUserId, int requestType) {
-        Preconditions.checkNotNull(launcherPackageName);
+        Objects.requireNonNull(launcherPackageName);
         String action = requestType == LauncherApps.PinItemRequest.REQUEST_TYPE_SHORTCUT ?
                 LauncherApps.ACTION_CONFIRM_PIN_SHORTCUT :
                 LauncherApps.ACTION_CONFIRM_PIN_APPWIDGET;
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 8c207a8..eab3f4d 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -75,7 +75,7 @@
 
         private PackageWithUser(int userId, String packageName) {
             this.userId = userId;
-            this.packageName = Preconditions.checkNotNull(packageName);
+            this.packageName = Objects.requireNonNull(packageName);
         }
 
         public static PackageWithUser of(int userId, String packageName) {
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 6c3eb31..9e462cd 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -50,6 +50,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.util.IntArray;
 import android.util.Slog;
@@ -58,6 +59,7 @@
 import android.util.apk.ApkSignatureVerifier;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageHelper;
 import com.android.internal.os.BackgroundThread;
 
 import java.io.File;
@@ -309,39 +311,100 @@
         return sessionContains(session, (s) -> !isApexSession(s));
     }
 
+    // Reverts apex sessions and user data (if checkpoint is supported). Also reboots the device.
+    private void abortCheckpoint() {
+        try {
+            if (supportsCheckpoint() && needsCheckpoint()) {
+                mApexManager.revertActiveSessions();
+                PackageHelper.getStorageManager().abortChanges(
+                        "StagingManager initiated", false /*retry*/);
+            }
+        } catch (Exception e) {
+            Slog.wtf(TAG, "Failed to abort checkpoint", e);
+            mApexManager.revertActiveSessions();
+            mPowerManager.reboot(null);
+        }
+    }
+
+    private boolean supportsCheckpoint() throws RemoteException {
+        return PackageHelper.getStorageManager().supportsCheckpoint();
+    }
+
+    private boolean needsCheckpoint() throws RemoteException {
+        return PackageHelper.getStorageManager().needsCheckpoint();
+    }
+
     private void resumeSession(@NonNull PackageInstallerSession session) {
         Slog.d(TAG, "Resuming session " + session.sessionId);
+
         final boolean hasApex = sessionContainsApex(session);
+        ApexSessionInfo apexSessionInfo = null;
         if (hasApex) {
             // Check with apexservice whether the apex packages have been activated.
-            ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+            apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId);
+
+            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.
+                // Greedily re-trigger the pre-reboot verification. We want to avoid marking it as
+                // failed when not in checkpoint mode, hence it is being processed separately.
+                Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to "
+                        + "be verified, resuming pre-reboot verification");
+                mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+                return;
+            }
+        }
+
+        // Before we resume session, we check if revert is needed or not. Typically, we enter file-
+        // system checkpoint mode when we reboot first time in order to install staged sessions. We
+        // want to install staged sessions in this mode as rebooting now will revert user data. If
+        // something goes wrong, then we reboot again to enter fs-rollback mode. Rebooting now will
+        // have no effect on user data, so mark the sessions as failed instead.
+        try {
+            // If checkpoint is supported, then we only resume sessions if we are in checkpointing
+            // mode. If not, we fail all sessions.
+            if (supportsCheckpoint() && !needsCheckpoint()) {
+                // TODO(b/146343545): Persist failure reason across checkpoint reboot
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                        "Reverting back to safe state");
+                return;
+            }
+        } catch (RemoteException e) {
+            // Cannot continue staged install without knowing if fs-checkpoint is supported
+            Slog.e(TAG, "Checkpoint support unknown. Aborting staged install for session "
+                    + session.sessionId, e);
+            // TODO: Mark all staged sessions together and reboot only once
+            session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                    "Checkpoint support unknown. Aborting staged install.");
+            if (hasApex) {
+                mApexManager.revertActiveSessions();
+            }
+            mPowerManager.reboot("Checkpoint support unknown");
+            return;
+        }
+
+        if (hasApex) {
             if (apexSessionInfo == null) {
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "apexd did not know anything about a staged session supposed to be"
                         + "activated");
+                abortCheckpoint();
                 return;
             }
             if (isApexSessionFailed(apexSessionInfo)) {
                 session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "APEX activation failed. Check logcat messages from apexd for "
                                 + "more information.");
-                return;
-            }
-            if (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.
-                // Greedily re-trigger the pre-reboot verification.
-                Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
-                        + "verified, resuming pre-reboot verification");
-                mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
+                abortCheckpoint();
                 return;
             }
             if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
-                // In all the remaining cases apexd will try to apply the session again at next
-                // boot. Nothing to do here for now.
-                Slog.w(TAG, "Staged session " + session.sessionId + " scheduled to be applied "
-                        + "at boot didn't activate nor fail. This usually means that apexd will "
-                        + "retry at next reboot.");
+                // Apexd did not apply the session for some unknown reason. There is no guarantee
+                // that apexd will install it next time. Safer to proactively mark as failed.
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
+                        "Staged session " + session.sessionId + "at boot didn't "
+                                + "activate nor fail. Marking it as failed anyway.");
+                abortCheckpoint();
                 return;
             }
             Slog.i(TAG, "APEX packages in session " + session.sessionId
@@ -353,7 +416,9 @@
             installApksInSession(session);
         } catch (PackageManagerException e) {
             session.setStagedSessionFailed(e.error, e.getMessage());
+            abortCheckpoint();
 
+            // If checkpoint is not supported, we have to handle failure for one staged session.
             if (!hasApex) {
                 return;
             }
@@ -1020,6 +1085,20 @@
          * </ul></p>
          */
         private void handlePreRebootVerification_End(@NonNull PackageInstallerSession session) {
+            // Before marking the session as ready, start checkpoint service if available
+            try {
+                IStorageManager storageManager = PackageHelper.getStorageManager();
+                if (storageManager.supportsCheckpoint()) {
+                    storageManager.startCheckpoint(1);
+                }
+            } catch (Exception e) {
+                // Failed to get hold of StorageManager
+                Slog.e(TAG, "Failed to get hold of StorageManager", e);
+                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                        "Failed to get hold of StorageManager");
+                return;
+            }
+
             // Proactively mark session as ready before calling apexd. Although this call order
             // looks counter-intuitive, this is the easiest way to ensure that session won't end up
             // in the inconsistent state:
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 59a5804..f5f4009 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -48,6 +48,44 @@
             "include-filter": "android.permission.cts.PermissionUpdateListenerTest"
         }
       ]
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "install-arg": "-t"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserDataPreparerTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserLifecycleStressTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserManagerServiceCreateProfileTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserManagerServiceIdRecyclingTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserManagerServiceTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserManagerServiceUserInfoTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserManagerServiceUserTypeTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserManagerTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserRestrictionsUtilsTest"
+        },
+        {
+          "include-filter": "com.android.server.pm.UserSystemPackageInstallerTest"
+        }
+      ]
     }
   ],
   "imports": [
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5854e32..e5d5b57 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -434,7 +434,7 @@
         private final IntentSender mTarget;
 
         public DisableQuietModeUserUnlockedCallback(IntentSender target) {
-            Preconditions.checkNotNull(target);
+            Objects.requireNonNull(target);
             mTarget = target;
         }
 
@@ -884,7 +884,7 @@
     @Override
     public boolean requestQuietModeEnabled(@NonNull String callingPackage, boolean enableQuietMode,
             @UserIdInt int userId, @Nullable IntentSender target) {
-        Preconditions.checkNotNull(callingPackage);
+        Objects.requireNonNull(callingPackage);
 
         if (enableQuietMode && target != null) {
             throw new IllegalArgumentException(
@@ -1132,14 +1132,13 @@
     }
 
     /**
-     * Returns the user type, e.g. {@link UserManager#USER_TYPE_FULL_GUEST}, of the given userId,
-     * or null if the user doesn't exist.
+     * Returns whether the given user (specified by userId) is of the given user type, such as
+     * {@link UserManager#USER_TYPE_FULL_GUEST}.
      */
     @Override
-    public @Nullable String getUserTypeForUser(@UserIdInt int userId) {
-        // TODO(b/142482943): Decide on the appropriate permission requirements.
-        checkManageOrInteractPermIfCallerInOtherProfileGroup(userId, "getUserTypeForUser");
-        return getUserTypeNoChecks(userId);
+    public boolean isUserOfType(@UserIdInt int userId, String userType) {
+        checkManageUsersPermission("check user type");
+        return userType != null && userType.equals(getUserTypeNoChecks(userId));
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 90bd947..815f7b4 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -51,6 +51,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -357,7 +358,7 @@
     }
 
     public static void merge(@NonNull Bundle dest, @Nullable Bundle in) {
-        Preconditions.checkNotNull(dest);
+        Objects.requireNonNull(dest);
         Preconditions.checkArgument(dest != in);
         if (in == null) {
             return;
@@ -661,7 +662,7 @@
 
     public static boolean isSettingRestrictedForUser(Context context, @NonNull String setting,
             int userId, String value, int callingUid) {
-        Preconditions.checkNotNull(setting);
+        Objects.requireNonNull(setting);
         final UserManager mUserManager = context.getSystemService(UserManager.class);
         String restriction;
         boolean checkAllUser = false;
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 486cfef..0caab6d 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -62,6 +62,7 @@
 
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.util.Objects;
 
 /**
  * A system service that provides access to runtime and compiler artifacts.
@@ -180,7 +181,7 @@
         }
 
         // Sanity checks on the arguments.
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(callback);
 
         boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
         if (!bootImageProfile) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 605f869..d921f31 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -796,7 +796,7 @@
 
     @Override
     public int checkPermission(String permName, String pkgName, int userId) {
-        // Not using Preconditions.checkNotNull() here for compatibility reasons.
+        // Not using Objects.requireNonNull() here for compatibility reasons.
         if (permName == null || pkgName == null) {
             return PackageManager.PERMISSION_DENIED;
         }
@@ -872,7 +872,7 @@
 
     @Override
     public int checkUidPermission(String permName, int uid) {
-        // Not using Preconditions.checkNotNull() here for compatibility reasons.
+        // Not using Objects.requireNonNull() here for compatibility reasons.
         if (permName == null) {
             return PackageManager.PERMISSION_DENIED;
         }
@@ -955,7 +955,7 @@
     @Override
     @Nullable public List<String> getWhitelistedRestrictedPermissions(@NonNull String packageName,
             @PermissionWhitelistFlags int flags, @UserIdInt int userId) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         Preconditions.checkFlagsArgument(flags,
                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
                         | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
@@ -1043,7 +1043,7 @@
             @NonNull String permName, @PermissionWhitelistFlags int flags,
             @UserIdInt int userId) {
         // Other argument checks are done in get/setWhitelistedRestrictedPermissions
-        Preconditions.checkNotNull(permName);
+        Objects.requireNonNull(permName);
 
         if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permName)) {
             return false;
@@ -1086,7 +1086,7 @@
             @NonNull String permName, @PermissionWhitelistFlags int flags,
             @UserIdInt int userId) {
         // Other argument checks are done in get/setWhitelistedRestrictedPermissions
-        Preconditions.checkNotNull(permName);
+        Objects.requireNonNull(permName);
 
         if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permName)) {
             return false;
@@ -1104,7 +1104,7 @@
     private boolean setWhitelistedRestrictedPermissionsInternal(@NonNull String packageName,
             @Nullable List<String> permissions, @PermissionWhitelistFlags int flags,
             @UserIdInt int userId) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         Preconditions.checkFlagsArgument(flags,
                 PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE
                         | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM
@@ -3025,11 +3025,10 @@
     @Override
     public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
             long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) {
-        mContext.enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
-                "Must be able to revoke runtime permissions to register permissions as one time.");
-        mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
-                "Must be able to access usage stats to register permissions as one time.");
-        packageName = Preconditions.checkNotNull(packageName);
+        mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+                "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
+                        + " to register permissions as one time.");
+        Objects.requireNonNull(packageName);
 
         long token = Binder.clearCallingIdentity();
         try {
@@ -3042,11 +3041,10 @@
 
     @Override
     public void stopOneTimePermissionSession(String packageName, @UserIdInt int userId) {
-        mContext.enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
-                "Must be able to revoke runtime permissions to remove permissions as one time.");
-        mContext.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
-                "Must be able to access usage stats to remove permissions as one time.");
-        Preconditions.checkNotNull(packageName);
+        mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+                "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
+                        + " to remove permissions as one time.");
+        Objects.requireNonNull(packageName);
 
         long token = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0d8f257..5a124a7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -48,34 +48,19 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
-import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
@@ -2182,52 +2167,6 @@
         }
     }
 
-    @Override
-    public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) {
-
-        // If this switch statement is modified, modify the comment in the declarations of
-        // the type in {@link WindowManager.LayoutParams} as well.
-        switch (attrs.type) {
-            default:
-                // These are the windows that by default are shown only to the user that created
-                // them. If this needs to be overridden, set
-                // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
-                // {@link WindowManager.LayoutParams}. Note that permission
-                // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
-                if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
-                    return true;
-                }
-                break;
-
-            // These are the windows that by default are shown to all users. However, to
-            // protect against spoofing, check permissions below.
-            case TYPE_APPLICATION_STARTING:
-            case TYPE_BOOT_PROGRESS:
-            case TYPE_DISPLAY_OVERLAY:
-            case TYPE_INPUT_CONSUMER:
-            case TYPE_KEYGUARD_DIALOG:
-            case TYPE_MAGNIFICATION_OVERLAY:
-            case TYPE_NAVIGATION_BAR:
-            case TYPE_NAVIGATION_BAR_PANEL:
-            case TYPE_PHONE:
-            case TYPE_POINTER:
-            case TYPE_PRIORITY_PHONE:
-            case TYPE_SEARCH_BAR:
-            case TYPE_STATUS_BAR:
-            case TYPE_STATUS_BAR_PANEL:
-            case TYPE_STATUS_BAR_SUB_PANEL:
-            case TYPE_SYSTEM_DIALOG:
-            case TYPE_VOLUME_OVERLAY:
-            case TYPE_PRESENTATION:
-            case TYPE_PRIVATE_PRESENTATION:
-            case TYPE_DOCK_DIVIDER:
-                break;
-        }
-
-        // Check if third party app has set window to system window type.
-        return mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW) != PERMISSION_GRANTED;
-    }
-
     void readLidState() {
         mDefaultDisplayPolicy.setLidState(mWindowManagerFuncs.getLidState());
     }
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index b0f22e4..f3a6018 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -145,7 +145,8 @@
                     }
                     @Override
                     public boolean mayAllowExtraAppOp() {
-                        return !shouldApplyRestriction && hasRequestedLegacyExternalStorage;
+                        return !shouldApplyRestriction && hasRequestedLegacyExternalStorage
+                                && targetSDK <= Build.VERSION_CODES.Q;
                     }
                     @Override
                     public boolean mayDenyExtraAppOpIfGranted() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index a2425a3..b014372 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -702,16 +702,6 @@
     public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp);
 
     /**
-     * Check permissions when adding a window.
-     *
-     * @param attrs The window's LayoutParams.
-     *
-     * @return True if the window may only be shown to the current user, false if the window can
-     * be shown on all users' windows.
-     */
-    public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs);
-
-    /**
      * After the window manager has computed the current configuration based
      * on its knowledge of the display and input devices, it gives the policy
      * a chance to adjust the information contained in it.  If you want to
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index f0e4625..4142e6f 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -49,6 +49,7 @@
 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
 
 import java.util.ArrayList;
+import java.util.Objects;
 
 /**
  * Responsible for battery saver mode transition logic.
@@ -237,7 +238,7 @@
     private PowerManager getPowerManager() {
         if (mPowerManager == null) {
             mPowerManager =
-                    Preconditions.checkNotNull(mContext.getSystemService(PowerManager.class));
+                    Objects.requireNonNull(mContext.getSystemService(PowerManager.class));
         }
         return mPowerManager;
     }
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index fdb14be..c36d5ef 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -17,8 +17,10 @@
 package com.android.server.recoverysystem;
 
 import android.content.Context;
+import android.content.IntentSender;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
+import android.os.Binder;
 import android.os.IRecoverySystem;
 import android.os.IRecoverySystemProgressListener;
 import android.os.PowerManager;
@@ -28,6 +30,9 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 import libcore.io.IoUtils;
@@ -45,7 +50,7 @@
  * triggers /system/bin/uncrypt via init to de-encrypt an OTA package on the
  * /data partition so that it can be accessed under the recovery image.
  */
-public class RecoverySystemService extends IRecoverySystem.Stub {
+public class RecoverySystemService extends IRecoverySystem.Stub implements RebootEscrowListener {
     private static final String TAG = "RecoverySystemService";
     private static final boolean DEBUG = false;
 
@@ -67,6 +72,10 @@
     private final Injector mInjector;
     private final Context mContext;
 
+    private boolean mPreparedForReboot;
+    private String mUnattendedRebootToken;
+    private IntentSender mPreparedForRebootIntentSender;
+
     static class Injector {
         protected final Context mContext;
 
@@ -78,6 +87,10 @@
             return mContext;
         }
 
+        public LockSettingsInternal getLockSettingsService() {
+            return LocalServices.getService(LockSettingsInternal.class);
+        }
+
         public PowerManager getPowerManager() {
             return (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         }
@@ -120,14 +133,23 @@
      * Handles the lifecycle events for the RecoverySystemService.
      */
     public static final class Lifecycle extends SystemService {
+        private RecoverySystemService mRecoverySystemService;
+
         public Lifecycle(Context context) {
             super(context);
         }
 
         @Override
+        public void onBootPhase(int phase) {
+            if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+                mRecoverySystemService.onSystemServicesReady();
+            }
+        }
+
+        @Override
         public void onStart() {
-            RecoverySystemService recoverySystemService = new RecoverySystemService(getContext());
-            publishBinderService(Context.RECOVERY_SERVICE, recoverySystemService);
+            mRecoverySystemService = new RecoverySystemService(getContext());
+            publishBinderService(Context.RECOVERY_SERVICE, mRecoverySystemService);
         }
     }
 
@@ -141,6 +163,11 @@
         mContext = injector.getContext();
     }
 
+    @VisibleForTesting
+    void onSystemServicesReady() {
+        mInjector.getLockSettingsService().setRebootEscrowListener(this);
+    }
+
     @Override // Binder call
     public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
         if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
@@ -255,6 +282,95 @@
         }
     }
 
+    @Override // Binder call
+    public boolean requestLskf(String updateToken, IntentSender intentSender) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+
+        if (updateToken == null) {
+            return false;
+        }
+
+        // No need to prepare again for the same token.
+        if (mPreparedForReboot && updateToken.equals(mUnattendedRebootToken)) {
+            return true;
+        }
+
+        mPreparedForReboot = false;
+        mUnattendedRebootToken = updateToken;
+        mPreparedForRebootIntentSender = intentSender;
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            mInjector.getLockSettingsService().prepareRebootEscrow();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return true;
+    }
+
+    @Override
+    public void onPreparedForReboot(boolean ready) {
+        if (mUnattendedRebootToken == null) {
+            Slog.w(TAG, "onPreparedForReboot called when mUnattendedRebootToken is null");
+        }
+
+        mPreparedForReboot = ready;
+        if (ready) {
+            sendPreparedForRebootIntentIfNeeded();
+        }
+    }
+
+    private void sendPreparedForRebootIntentIfNeeded() {
+        final IntentSender intentSender = mPreparedForRebootIntentSender;
+        if (intentSender != null) {
+            try {
+                intentSender.sendIntent(null, 0, null, null, null);
+            } catch (IntentSender.SendIntentException e) {
+                Slog.w(TAG, "Could not send intent for prepared reboot: " + e.getMessage());
+            }
+        }
+    }
+
+    @Override // Binder call
+    public boolean clearLskf() {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+
+        mPreparedForReboot = false;
+        mUnattendedRebootToken = null;
+        mPreparedForRebootIntentSender = null;
+
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            mInjector.getLockSettingsService().clearRebootEscrow();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
+        return true;
+    }
+
+    @Override // Binder call
+    public boolean rebootWithLskf(String updateToken, String reason) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+
+        if (!mPreparedForReboot) {
+            return false;
+        }
+
+        if (updateToken != null && updateToken.equals(mUnattendedRebootToken)) {
+            if (!mInjector.getLockSettingsService().armRebootEscrow()) {
+                return false;
+            }
+
+            PowerManager pm = mInjector.getPowerManager();
+            pm.reboot(reason);
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Check if any of the init services is still running. If so, we cannot
      * start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index c4522e0..392792d 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -511,7 +511,7 @@
 
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
             Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
-            Preconditions.checkNotNull(callback, "callback cannot be null");
+            Objects.requireNonNull(callback, "callback cannot be null");
 
             getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags,
                     callback);
@@ -531,7 +531,7 @@
 
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
             Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
-            Preconditions.checkNotNull(callback, "callback cannot be null");
+            Objects.requireNonNull(callback, "callback cannot be null");
 
             getOrCreateController(userId).onRemoveRoleHolder(roleName, packageName, flags,
                     callback);
@@ -550,7 +550,7 @@
                     "clearRoleHoldersAsUser");
 
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
-            Preconditions.checkNotNull(callback, "callback cannot be null");
+            Objects.requireNonNull(callback, "callback cannot be null");
 
             getOrCreateController(userId).onClearRoleHolders(roleName, flags, callback);
         }
@@ -566,7 +566,7 @@
             getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
                     "addOnRoleHoldersChangedListenerAsUser");
 
-            Preconditions.checkNotNull(listener, "listener cannot be null");
+            Objects.requireNonNull(listener, "listener cannot be null");
 
             RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners(
                     userId);
@@ -584,7 +584,7 @@
             getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
                     "removeOnRoleHoldersChangedListenerAsUser");
 
-            Preconditions.checkNotNull(listener, "listener cannot be null");
+            Objects.requireNonNull(listener, "listener cannot be null");
 
             RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
             if (listener == null) {
@@ -599,7 +599,7 @@
                     RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                     "setRoleNamesFromController");
 
-            Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+            Objects.requireNonNull(roleNames, "roleNames cannot be null");
 
             int userId = UserHandle.getCallingUserId();
             getOrCreateUserState(userId).setRoleNames(roleNames);
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 1123f70..e6e6e23 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.server.rollback;
 
+import android.content.pm.PackageManager;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
 import android.os.storage.StorageManager;
@@ -119,11 +120,22 @@
         }
 
         try {
-            mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(), appId, seInfo,
-                    userId, rollbackId, storageFlags);
+            switch (packageRollbackInfo.getRollbackDataPolicy()) {
+                case PackageManager.RollbackDataPolicy.WIPE:
+                    mInstaller.clearAppData(null, packageRollbackInfo.getPackageName(),
+                            userId, storageFlags, 0);
+                    break;
+                case PackageManager.RollbackDataPolicy.RESTORE:
+                    mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(), appId,
+                            seInfo, userId, rollbackId, storageFlags);
+                    break;
+                default:
+                    break;
+            }
         } catch (InstallerException ie) {
-            Slog.e(TAG, "Unable to restore app data snapshot: "
-                        + packageRollbackInfo.getPackageName(), ie);
+            Slog.e(TAG, "Unable to restore/wipe app data: "
+                    + packageRollbackInfo.getPackageName() + " policy="
+                    + packageRollbackInfo.getRollbackDataPolicy(), ie);
         }
 
         return changedRollback;
@@ -207,13 +219,24 @@
 
             if (hasPendingRestore) {
                 try {
-                    mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
-                            ri.seInfo, userId, rollback.info.getRollbackId(),
-                            Installer.FLAG_STORAGE_CE);
+                    switch (info.getRollbackDataPolicy()) {
+                        case PackageManager.RollbackDataPolicy.WIPE:
+                            mInstaller.clearAppData(null, info.getPackageName(), userId,
+                                    Installer.FLAG_STORAGE_CE, 0);
+                            break;
+                        case PackageManager.RollbackDataPolicy.RESTORE:
+                            mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
+                                    ri.seInfo, userId, rollback.info.getRollbackId(),
+                                    Installer.FLAG_STORAGE_CE);
+                            break;
+                        default:
+                            break;
+                    }
                     info.removeRestoreInfo(ri);
                 } catch (InstallerException ie) {
-                    Slog.e(TAG, "Unable to restore app data snapshot for: "
-                            + info.getPackageName(), ie);
+                    Slog.e(TAG, "Unable to restore/wipe app data for: "
+                            + info.getPackageName() + " policy="
+                            + info.getRollbackDataPolicy(), ie);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 6898e1c..88c1564 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -306,7 +306,7 @@
      * @return boolean True if the rollback was enabled successfully for the specified package.
      */
     boolean enableForPackage(String packageName, long newVersion, long installedVersion,
-            boolean isApex, String sourceDir, String[] splitSourceDirs) {
+            boolean isApex, String sourceDir, String[] splitSourceDirs, int rollbackDataPolicy) {
         try {
             RollbackStore.backupPackageCodePath(this, packageName, sourceDir);
             if (!ArrayUtils.isEmpty(splitSourceDirs)) {
@@ -323,7 +323,8 @@
                 new VersionedPackage(packageName, newVersion),
                 new VersionedPackage(packageName, installedVersion),
                 new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
-                isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
+                isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */,
+                rollbackDataPolicy);
 
         synchronized (mLock) {
             info.getPackages().add(packageRollbackInfo);
@@ -344,10 +345,12 @@
 
             for (PackageRollbackInfo pkgRollbackInfo : info.getPackages()) {
                 if (pkgRollbackInfo.getPackageName().equals(packageName)) {
-                    dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds);
-
-                    RollbackStore.saveRollback(this);
-                    pkgRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
+                    if (pkgRollbackInfo.getRollbackDataPolicy()
+                            == PackageManager.RollbackDataPolicy.RESTORE) {
+                        dataHelper.snapshotAppData(info.getRollbackId(), pkgRollbackInfo, userIds);
+                        pkgRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
+                        RollbackStore.saveRollback(this);
+                    }
                     break;
                 }
             }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 6cdfcff..e29d1a7 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -17,8 +17,10 @@
 package com.android.server.rollback;
 
 import android.Manifest;
+import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -81,6 +83,11 @@
 
 /**
  * Implementation of service that manages APK level rollbacks.
+ *
+ * Threading model:
+ *
+ * - @AnyThread annotates thread-safe methods.
+ * - @WorkerThread annotates methods that should be called from the handler thread only.
  */
 class RollbackManagerServiceImpl extends IRollbackManager.Stub {
 
@@ -256,6 +263,7 @@
         registerTimeChangeReceiver();
     }
 
+    @AnyThread
     private void registerUserCallbacks(UserHandle user) {
         Context context = getContextAsUser(user);
         if (context == null) {
@@ -338,6 +346,7 @@
                     callerPackageName, statusReceiver));
     }
 
+    @AnyThread
     private void registerTimeChangeReceiver() {
         final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() {
             @Override
@@ -362,6 +371,7 @@
                 null /* broadcastPermission */, getHandler());
     }
 
+    @AnyThread
     private static long calculateRelativeBootTime() {
         return System.currentTimeMillis() - SystemClock.elapsedRealtime();
     }
@@ -371,6 +381,7 @@
      * The work is done on the current thread. This may be a long running
      * operation.
      */
+    @WorkerThread
     private void commitRollbackInternal(int rollbackId, List<VersionedPackage> causePackages,
             String callerPackageName, IntentSender statusReceiver) {
         Slog.i(TAG, "commitRollback id=" + rollbackId + " caller=" + callerPackageName);
@@ -440,6 +451,7 @@
         });
     }
 
+    @WorkerThread
     private void queueSleepIfNeeded() {
         if (mSleepDuration.size() == 0) {
             return;
@@ -486,6 +498,7 @@
         }
     }
 
+    @WorkerThread
     private void updateRollbackLifetimeDurationInMillis() {
         mRollbackLifetimeDurationInMillis = DeviceConfig.getLong(
                 DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
@@ -496,6 +509,7 @@
         }
     }
 
+    @AnyThread
     void onBootCompleted() {
         getHandler().post(() -> updateRollbackLifetimeDurationInMillis());
         // Also posts to handler thread
@@ -564,6 +578,7 @@
      * Removes all backups for the package not matching the currently
      * installed package version.
      */
+    @WorkerThread
     private void onPackageReplaced(String packageName) {
         // TODO: Could this end up incorrectly deleting a rollback for a
         // package that is about to be installed?
@@ -588,6 +603,7 @@
      * Called when a package has been completely removed from the device.
      * Removes all backups and rollback history for the given package.
      */
+    @WorkerThread
     private void onPackageFullyRemoved(String packageName) {
         expireRollbackForPackage(packageName);
     }
@@ -599,6 +615,7 @@
      * @param status the RollbackManager.STATUS_* code with the failure.
      * @param message the failure message.
      */
+    @AnyThread
     static void sendFailure(Context context, IntentSender statusReceiver,
             @RollbackManager.Status int status, String message) {
         Slog.e(TAG, message);
@@ -614,6 +631,7 @@
 
     // Check to see if anything needs expiration, and if so, expire it.
     // Schedules future expiration as appropriate.
+    @WorkerThread
     private void runExpiration() {
         Instant now = Instant.now();
         Instant oldest = null;
@@ -649,10 +667,12 @@
      * Schedules an expiration check to be run after the given duration in
      * milliseconds has gone by.
      */
+    @AnyThread
     private void scheduleExpiration(long duration) {
         getHandler().postDelayed(() -> runExpiration(), duration);
     }
 
+    @AnyThread
     private Handler getHandler() {
         return mHandlerThread.getThreadHandler();
     }
@@ -660,6 +680,7 @@
     // Returns true if <code>session</code> has installFlags and code path
     // matching the installFlags and new package code path given to
     // enableRollback.
+    @WorkerThread
     private boolean sessionMatchesForEnableRollback(PackageInstaller.SessionInfo session,
             int installFlags, File newPackageCodePath) {
         if (session == null || session.resolvedBaseCodePath == null) {
@@ -674,6 +695,7 @@
         return false;
     }
 
+    @AnyThread
     private Context getContextAsUser(UserHandle user) {
         try {
             return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, user);
@@ -693,6 +715,7 @@
      * @param token the distinct rollback token sent by package manager.
      * @return true if enabling the rollback succeeds, false otherwise.
      */
+    @WorkerThread
     private boolean enableRollback(
             int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
         if (LOCAL_LOGV) {
@@ -780,6 +803,34 @@
         return enableRollbackForPackageSession(newRollback.rollback, packageSession);
     }
 
+    @WorkerThread
+    private void removeRollbackForPackageSessionId(int sessionId) {
+        if (LOCAL_LOGV) {
+            Slog.v(TAG, "removeRollbackForPackageSessionId=" + sessionId);
+        }
+
+        synchronized (mLock) {
+            NewRollback newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+            if (newRollback != null) {
+                Slog.w(TAG, "Delete new rollback id=" + newRollback.rollback.info.getRollbackId()
+                        + " for session id=" + sessionId);
+                mNewRollbacks.remove(newRollback);
+                newRollback.rollback.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
@@ -787,6 +838,7 @@
      *
      * @return true on success, false on failure.
      */
+    @WorkerThread
     private boolean enableRollbackForPackageSession(Rollback rollback,
             PackageInstaller.SessionInfo session) {
         // TODO: Don't attempt to enable rollback for split installs.
@@ -841,7 +893,7 @@
         ApplicationInfo appInfo = pkgInfo.applicationInfo;
         return rollback.enableForPackage(packageName, newPackage.versionCode,
                 pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
-                appInfo.splitSourceDirs);
+                appInfo.splitSourceDirs, session.rollbackDataPolicy);
     }
 
     @Override
@@ -861,6 +913,7 @@
         });
     }
 
+    @WorkerThread
     private void snapshotUserDataInternal(String packageName, int[] userIds) {
         if (LOCAL_LOGV) {
             Slog.v(TAG, "snapshotUserData pkg=" + packageName
@@ -880,6 +933,7 @@
         }
     }
 
+    @WorkerThread
     private void restoreUserDataInternal(
             String packageName, int[] userIds, int appId, String seInfo) {
         if (LOCAL_LOGV) {
@@ -944,7 +998,7 @@
                 }
             }
 
-            Rollback rollback = completeEnableRollback(newRollback, true);
+            Rollback rollback = completeEnableRollback(newRollback);
             if (rollback == null) {
                 result.offer(-1);
             } else {
@@ -994,6 +1048,7 @@
      * Returns true if the installer is allowed to enable rollback for the
      * given named package, false otherwise.
      */
+    @AnyThread
     private boolean enableRollbackAllowed(String installerPackageName, String packageName) {
         if (installerPackageName == null) {
             return false;
@@ -1016,6 +1071,7 @@
     /**
      * Returns true is this package is eligible for enabling rollback.
      */
+    @AnyThread
     private boolean isRollbackWhitelisted(String packageName) {
         // TODO: Remove #isModule when the white list is ready.
         return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName)
@@ -1024,6 +1080,7 @@
     /**
      * Returns true if the package name is the name of a module.
      */
+    @AnyThread
     private boolean isModule(String packageName) {
         PackageManager pm = mContext.getPackageManager();
         final ModuleInfo moduleInfo;
@@ -1040,6 +1097,7 @@
      * Gets the version of the package currently installed.
      * Returns -1 if the package is not currently installed.
      */
+    @AnyThread
     private long getInstalledPackageVersion(String packageName) {
         PackageInfo pkgInfo;
         try {
@@ -1056,6 +1114,7 @@
      *
      * @throws PackageManager.NameNotFoundException if no such package is installed.
      */
+    @AnyThread
     private PackageInfo getPackageInfo(String packageName)
             throws PackageManager.NameNotFoundException {
         PackageManager pm = mContext.getPackageManager();
@@ -1070,6 +1129,7 @@
         }
     }
 
+    @WorkerThread
     private class SessionCallback extends PackageInstaller.SessionCallback {
 
         @Override
@@ -1089,23 +1149,29 @@
             if (LOCAL_LOGV) {
                 Slog.v(TAG, "SessionCallback.onFinished id=" + sessionId + " success=" + success);
             }
-            NewRollback newRollback;
-            synchronized (mLock) {
-                newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+
+            if (success) {
+                NewRollback newRollback;
+                synchronized (mLock) {
+                    newRollback = getNewRollbackForPackageSessionLocked(sessionId);
+                    if (newRollback != null && newRollback.notifySessionWithSuccess()) {
+                        mNewRollbacks.remove(newRollback);
+                    } else {
+                        // Not all child sessions finished with success.
+                        // Don't enable the rollback yet.
+                        newRollback = null;
+                    }
+                }
+
                 if (newRollback != null) {
-                    mNewRollbacks.remove(newRollback);
+                    Rollback rollback = completeEnableRollback(newRollback);
+                    if (rollback != null && !rollback.isStaged()) {
+                        makeRollbackAvailable(rollback);
+                    }
                 }
+            } else {
+                removeRollbackForPackageSessionId(sessionId);
             }
-
-            if (newRollback != null) {
-                Rollback rollback = completeEnableRollback(newRollback, success);
-                if (rollback != null && !rollback.isStaged()) {
-                    makeRollbackAvailable(rollback);
-                }
-            }
-
-            // Clear the queue so it will never be leaked to next tests.
-            mSleepDuration.clear();
         }
     }
 
@@ -1116,16 +1182,11 @@
      * @return the Rollback instance for a successfully enable-completed rollback,
      * or null on error.
      */
-    private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
+    @WorkerThread
+    private Rollback completeEnableRollback(NewRollback newRollback) {
         Rollback rollback = newRollback.rollback;
         if (LOCAL_LOGV) {
-            Slog.v(TAG, "completeEnableRollback id="
-                    + rollback.info.getRollbackId() + " success=" + success);
-        }
-        if (!success) {
-            // The install session was aborted, clean up the pending install.
-            rollback.delete(mAppDataRollbackHelper);
-            return null;
+            Slog.v(TAG, "completeEnableRollback id=" + rollback.info.getRollbackId());
         }
 
         if (newRollback.isCancelled()) {
@@ -1158,6 +1219,7 @@
         return rollback;
     }
 
+    @WorkerThread
     @GuardedBy("rollback.getLock")
     private void makeRollbackAvailable(Rollback rollback) {
         if (LOCAL_LOGV) {
@@ -1178,6 +1240,7 @@
     /*
      * Returns the rollback with the given rollbackId, if any.
      */
+    @WorkerThread
     private Rollback getRollbackForId(int rollbackId) {
         synchronized (mLock) {
             for (int i = 0; i < mRollbacks.size(); ++i) {
@@ -1191,6 +1254,7 @@
         return null;
     }
 
+    @WorkerThread
     @GuardedBy("mLock")
     private int allocateRollbackIdLocked() {
         int n = 0;
@@ -1220,6 +1284,7 @@
         }
     }
 
+    @AnyThread
     private void enforceManageRollbacks(@NonNull String message) {
         if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
                         Manifest.permission.MANAGE_ROLLBACKS))
@@ -1251,6 +1316,14 @@
         @GuardedBy("mNewRollbackLock")
         private boolean mIsCancelled = false;
 
+        /**
+         * The number of sessions in the install which are notified with success by
+         * {@link PackageInstaller.SessionCallback#onFinished(int, boolean)}.
+         * This NewRollback will be enabled only after all child sessions finished with success.
+         */
+        @GuardedBy("mNewRollbackLock")
+        private int mNumPackageSessionsWithSuccess;
+
         private final Object mNewRollbackLock = new Object();
 
         NewRollback(Rollback rollback, int[] packageSessionIds) {
@@ -1322,8 +1395,20 @@
         int getPackageSessionIdCount() {
             return mPackageSessionIds.length;
         }
+
+        /**
+         * Called when a child session finished with success.
+         * Returns true when all child sessions are notified with success. This NewRollback will be
+         * enabled only after all child sessions finished with success.
+         */
+        boolean notifySessionWithSuccess() {
+            synchronized (mNewRollbackLock) {
+                return ++mNumPackageSessionsWithSuccess == mPackageSessionIds.length;
+            }
+        }
     }
 
+    @WorkerThread
     @GuardedBy("mLock")
     private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
         int rollbackId = allocateRollbackIdLocked();
@@ -1365,6 +1450,7 @@
      * Returns null if no NewRollback is found for the given package
      * session.
      */
+    @WorkerThread
     @GuardedBy("mLock")
     NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
         // We expect mNewRollbacks to be a very small list; linear search
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index bb09584..a0ef8cf 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -120,9 +120,7 @@
 
         RollbackInfo rollback = getAvailableRollback(failedPackage);
         if (rollback == null) {
-            Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
-                    + failedPackage.getPackageName() + "] with versionCode: ["
-                    + failedPackage.getVersionCode() + "]");
+            Slog.w(TAG, "Expected rollback but no valid rollback found for " + failedPackage);
             return false;
         }
         rollbackPackage(rollback, failedPackage, rollbackReason);
@@ -212,11 +210,7 @@
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
             for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
-                boolean hasFailedPackage = packageRollback.getPackageName().equals(
-                        failedPackage.getPackageName())
-                        && packageRollback.getVersionRolledBackFrom().getVersionCode()
-                        == failedPackage.getVersionCode();
-                if (hasFailedPackage) {
+                if (packageRollback.getVersionRolledBackFrom().equals(failedPackage)) {
                     return rollback;
                 }
             }
@@ -339,9 +333,24 @@
         return rollbackId;
     }
 
+    private static String rollbackTypeToString(int type) {
+        switch (type) {
+            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE:
+                return "ROLLBACK_INITIATE";
+            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS:
+                return "ROLLBACK_SUCCESS";
+            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE:
+                return "ROLLBACK_FAILURE";
+            case StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_BOOT_TRIGGERED:
+                return "ROLLBACK_BOOT_TRIGGERED";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
     private static void logEvent(@Nullable VersionedPackage moduleMetadataPackage, int type,
             int rollbackReason, @NonNull String failingPackageName) {
-        Slog.i(TAG, "Watchdog event occurred of type: " + type);
+        Slog.i(TAG, "Watchdog event occurred of type: " + rollbackTypeToString(type));
         if (moduleMetadataPackage != null) {
             StatsLog.logWatchdogRollbackOccurred(type, moduleMetadataPackage.getPackageName(),
                     moduleMetadataPackage.getVersionCode(), rollbackReason, failingPackageName);
@@ -361,15 +370,6 @@
         }
     }
 
-    private VersionedPackage getVersionedPackage(String packageName) {
-        try {
-            return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
-                    packageName, 0 /* flags */).getLongVersionCode());
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-    }
-
     /**
      * Rolls back the session that owns {@code failedPackage}
      *
@@ -432,14 +432,8 @@
         List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
 
         for (RollbackInfo rollback : rollbacks) {
-            String samplePackageName = rollback.getPackages().get(0).getPackageName();
-            VersionedPackage sampleVersionedPackage = getVersionedPackage(samplePackageName);
-            if (sampleVersionedPackage == null) {
-                Slog.e(TAG, "Failed to rollback " + samplePackageName);
-                continue;
-            }
-            rollbackPackage(rollback, sampleVersionedPackage,
-                    PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+            VersionedPackage sample = rollback.getPackages().get(0).getVersionRolledBackFrom();
+            rollbackPackage(rollback, sample, PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
         }
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 550c754..df75a29 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -21,6 +21,7 @@
 import static com.android.server.rollback.Rollback.rollbackStateFromString;
 
 import android.annotation.NonNull;
+import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.PackageRollbackInfo.RestoreInfo;
@@ -345,6 +346,8 @@
         json.put("installedUsers", convertToJsonArray(snapshottedUsers));
         json.put("ceSnapshotInodes", ceSnapshotInodesToJson(info.getCeSnapshotInodes()));
 
+        json.put("rollbackDataPolicy", info.getRollbackDataPolicy());
+
         return json;
     }
 
@@ -367,8 +370,13 @@
         final SparseLongArray ceSnapshotInodes = ceSnapshotInodesFromJson(
                 json.getJSONArray("ceSnapshotInodes"));
 
+        // Backward compatibility: no such field for old versions.
+        final int rollbackDataPolicy = json.optInt("rollbackDataPolicy",
+                PackageManager.RollbackDataPolicy.RESTORE);
+
         return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
-                pendingBackups, pendingRestores, isApex, snapshottedUsers, ceSnapshotInodes);
+                pendingBackups, pendingRestores, isApex, snapshottedUsers, ceSnapshotInodes,
+                rollbackDataPolicy);
     }
 
     private static JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index 0bbb179..e90c02a 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -59,7 +59,7 @@
         }
 
         @Override
-        public boolean isAppSourceCertificateTrusted(byte[] certificateBytes) {
+        public boolean isAppSourceCertificateTrusted(@Nullable byte[] certificateBytes) {
             enforceAnyCallingPermissions(
                     android.Manifest.permission.REQUEST_INSTALL_PACKAGES,
                     android.Manifest.permission.INSTALL_PACKAGES);
@@ -67,7 +67,10 @@
                 if (!isApkVeritySupported()) {
                     return false;
                 }
-
+                if (certificateBytes == null) {
+                    Slog.w(TAG, "Received a null certificate");
+                    return false;
+                }
                 return mTrustedCertificates.contains(toCertificate(certificateBytes));
             } catch (CertificateException e) {
                 Slog.e(TAG, "Failed to convert the certificate: " + e);
@@ -122,7 +125,12 @@
             }
 
             for (File cert : files) {
-                collectCertificate(Files.readAllBytes(cert.toPath()));
+                byte[] certificateBytes = Files.readAllBytes(cert.toPath());
+                if (certificateBytes == null) {
+                    Slog.w(TAG, "The certificate file is empty, ignoring " + cert);
+                    continue;
+                }
+                collectCertificate(certificateBytes);
             }
         } catch (IOException e) {
             Slog.wtf(TAG, "Failed to load fs-verity certificate from " + path, e);
@@ -146,10 +154,10 @@
      * Tries to convert {@code bytes} into an X.509 certificate and store in memory.
      * Errors need to be surpressed in order fo the next certificates to still be collected.
      */
-    private void collectCertificate(@Nullable byte[] bytes) {
+    private void collectCertificate(@NonNull byte[] bytes) {
         try {
             mTrustedCertificates.add(toCertificate(bytes));
-        } catch (CertificateException | AssertionError e) {
+        } catch (CertificateException e) {
             Slog.e(TAG, "Invalid certificate, ignored: " + e);
         }
     }
@@ -159,7 +167,7 @@
      * the rest. The rational is to make it harder to smuggle.
      */
     @NonNull
-    private static X509Certificate toCertificate(@Nullable byte[] bytes)
+    private static X509Certificate toCertificate(@NonNull byte[] bytes)
             throws CertificateException {
         Certificate certificate = sCertFactory.generateCertificate(new ByteArrayInputStream(bytes));
         if (!(certificate instanceof X509Certificate)) {
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 3b2f324..7c8c494 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -106,7 +106,7 @@
     @VisibleForTesting
     SliceManagerService(Context context, Looper looper) {
         mContext = context;
-        mPackageManagerInternal = Preconditions.checkNotNull(
+        mPackageManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(PackageManagerInternal.class));
         mAppOps = context.getSystemService(AppOpsManager.class);
         mAssistUtils = new AssistUtils(context);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 9b22f33..0b89646 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -40,7 +40,6 @@
 import android.os.HidlMemoryUtil;
 
 import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Utilities for type conversion between SoundTrigger HAL types and SoundTriggerMiddleware service
@@ -60,7 +59,8 @@
         aidlProperties.maxSoundModels = hidlProperties.maxSoundModels;
         aidlProperties.maxKeyPhrases = hidlProperties.maxKeyPhrases;
         aidlProperties.maxUsers = hidlProperties.maxUsers;
-        aidlProperties.recognitionModes = hidlProperties.recognitionModes;
+        aidlProperties.recognitionModes =
+                hidl2aidlRecognitionModes(hidlProperties.recognitionModes);
         aidlProperties.captureTransition = hidlProperties.captureTransition;
         aidlProperties.maxBufferMs = hidlProperties.maxBufferMs;
         aidlProperties.concurrentCapture = hidlProperties.concurrentCapture;
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
index a7cfe10..987c05f 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -42,6 +42,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -179,8 +180,8 @@
         // Permission check.
         checkPermissions();
         // Input validation.
-        Preconditions.checkNotNull(callback);
-        Preconditions.checkNotNull(callback.asBinder());
+        Objects.requireNonNull(callback);
+        Objects.requireNonNull(callback.asBinder());
 
         synchronized (this) {
             // State validation.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
index 4898e6b..43047d1 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ValidationUtil.java
@@ -29,6 +29,7 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -42,7 +43,7 @@
  */
 public class ValidationUtil {
     static void validateUuid(@Nullable String uuid) {
-        Preconditions.checkNotNull(uuid);
+        Objects.requireNonNull(uuid);
         Matcher matcher = UuidUtil.PATTERN.matcher(uuid);
         if (!matcher.matches()) {
             throw new IllegalArgumentException(
@@ -55,37 +56,37 @@
     }
 
     static void validateModel(@Nullable SoundModel model, int expectedType) {
-        Preconditions.checkNotNull(model);
+        Objects.requireNonNull(model);
         if (model.type != expectedType) {
             throw new IllegalArgumentException("Invalid type");
         }
         validateUuid(model.uuid);
         validateUuid(model.vendorUuid);
-        Preconditions.checkNotNull(model.data);
+        Objects.requireNonNull(model.data);
     }
 
     static void validatePhraseModel(@Nullable PhraseSoundModel model) {
-        Preconditions.checkNotNull(model);
+        Objects.requireNonNull(model);
         validateModel(model.common, SoundModelType.KEYPHRASE);
-        Preconditions.checkNotNull(model.phrases);
+        Objects.requireNonNull(model.phrases);
         for (Phrase phrase : model.phrases) {
-            Preconditions.checkNotNull(phrase);
+            Objects.requireNonNull(phrase);
             if ((phrase.recognitionModes & ~(RecognitionMode.VOICE_TRIGGER
                     | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.USER_AUTHENTICATION
                     | RecognitionMode.GENERIC_TRIGGER)) != 0) {
                 throw new IllegalArgumentException("Invalid recognitionModes");
             }
-            Preconditions.checkNotNull(phrase.users);
-            Preconditions.checkNotNull(phrase.locale);
-            Preconditions.checkNotNull(phrase.text);
+            Objects.requireNonNull(phrase.users);
+            Objects.requireNonNull(phrase.locale);
+            Objects.requireNonNull(phrase.text);
         }
     }
 
     static void validateRecognitionConfig(@Nullable RecognitionConfig config) {
-        Preconditions.checkNotNull(config);
-        Preconditions.checkNotNull(config.phraseRecognitionExtras);
+        Objects.requireNonNull(config);
+        Objects.requireNonNull(config.phraseRecognitionExtras);
         for (PhraseRecognitionExtra extra : config.phraseRecognitionExtras) {
-            Preconditions.checkNotNull(extra);
+            Objects.requireNonNull(extra);
             if ((extra.recognitionModes & ~(RecognitionMode.VOICE_TRIGGER
                     | RecognitionMode.USER_IDENTIFICATION | RecognitionMode.USER_AUTHENTICATION
                     | RecognitionMode.GENERIC_TRIGGER)) != 0) {
@@ -94,15 +95,15 @@
             if (extra.confidenceLevel < 0 || extra.confidenceLevel > 100) {
                 throw new IllegalArgumentException("Invalid confidenceLevel");
             }
-            Preconditions.checkNotNull(extra.levels);
+            Objects.requireNonNull(extra.levels);
             for (ConfidenceLevel level : extra.levels) {
-                Preconditions.checkNotNull(level);
+                Objects.requireNonNull(level);
                 if (level.levelPercent < 0 || level.levelPercent > 100) {
                     throw new IllegalArgumentException("Invalid confidenceLevel");
                 }
             }
         }
-        Preconditions.checkNotNull(config.data);
+        Objects.requireNonNull(config.data);
     }
 
     static void validateModelParameter(int modelParam) {
diff --git a/services/core/java/com/android/server/stats/StatsPullAtomService.java b/services/core/java/com/android/server/stats/StatsPullAtomService.java
new file mode 100644
index 0000000..e367f28
--- /dev/null
+++ b/services/core/java/com/android/server/stats/StatsPullAtomService.java
@@ -0,0 +1,58 @@
+/*
+ * 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.stats;
+
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+
+/**
+ * SystemService containing PullAtomCallbacks that are registered with statsd.
+ *
+ * @hide
+ */
+public class StatsPullAtomService extends SystemService {
+    private static final String TAG = "StatsPullAtomService";
+    private static final boolean DEBUG = true;
+
+    public StatsPullAtomService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        // No op.
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        super.onBootPhase(phase);
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            BackgroundThread.getHandler().post(() -> {
+                registerAllPullers();
+            });
+        }
+    }
+
+    void registerAllPullers() {
+        if (DEBUG) {
+            Slog.d(TAG, "Registering all pullers with statsd");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/storage/AppCollector.java b/services/core/java/com/android/server/storage/AppCollector.java
index 0b51f9c..ecbc88f 100644
--- a/services/core/java/com/android/server/storage/AppCollector.java
+++ b/services/core/java/com/android/server/storage/AppCollector.java
@@ -59,7 +59,7 @@
      * @param volume Volume to check for apps.
      */
     public AppCollector(Context context, @NonNull VolumeInfo volume) {
-        Preconditions.checkNotNull(volume);
+        Objects.requireNonNull(volume);
 
         mBackgroundHandler = new BackgroundHandler(BackgroundThread.get().getLooper(),
                 volume,
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index 60de10c..aafadf9 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -66,6 +66,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * CacheQuotaStrategy is a strategy for determining cache quotas using usage stats and foreground
@@ -95,10 +96,10 @@
     public CacheQuotaStrategy(
             Context context, UsageStatsManagerInternal usageStatsManager, Installer installer,
             ArrayMap<String, SparseLongArray> quotaMap) {
-        mContext = Preconditions.checkNotNull(context);
-        mUsageStats = Preconditions.checkNotNull(usageStatsManager);
-        mInstaller = Preconditions.checkNotNull(installer);
-        mQuotaMap = Preconditions.checkNotNull(quotaMap);
+        mContext = Objects.requireNonNull(context);
+        mUsageStats = Objects.requireNonNull(usageStatsManager);
+        mInstaller = Objects.requireNonNull(installer);
+        mQuotaMap = Objects.requireNonNull(quotaMap);
         mPreviousValuesFile = new AtomicFile(new File(
                 new File(Environment.getDataDirectory(), "system"), "cachequota.xml"));
     }
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index aa3ab63..f4fb93a 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -43,6 +43,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
+import java.util.Objects;
 
 /**
  * Controls storage sessions for users initiated by the {@link StorageManagerService}.
@@ -63,7 +64,7 @@
     private volatile boolean mIsResetting;
 
     public StorageSessionController(Context context, boolean isFuseEnabled) {
-        mContext = Preconditions.checkNotNull(context);
+        mContext = Objects.requireNonNull(context);
         mIsFuseEnabled = isFuseEnabled;
     }
 
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 10514ad..c02ded8 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -48,6 +48,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -69,7 +70,7 @@
     @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
 
     public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
-        mContext = Preconditions.checkNotNull(context);
+        mContext = Objects.requireNonNull(context);
         mUserId = Preconditions.checkArgumentNonnegative(userId);
         mSessionController = controller;
     }
@@ -83,10 +84,10 @@
      */
     public void startSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
             String lowerPath) throws ExternalStorageServiceException {
-        Preconditions.checkNotNull(sessionId);
-        Preconditions.checkNotNull(pfd);
-        Preconditions.checkNotNull(upperPath);
-        Preconditions.checkNotNull(lowerPath);
+        Objects.requireNonNull(sessionId);
+        Objects.requireNonNull(pfd);
+        Objects.requireNonNull(upperPath);
+        Objects.requireNonNull(lowerPath);
 
         prepareRemote();
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 6a986b9..5283cc4 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -62,6 +62,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Queue;
 
 /**
@@ -137,7 +138,7 @@
     private TextClassificationConstants mSettings;
 
     private TextClassificationManagerService(Context context) {
-        mContext = Preconditions.checkNotNull(context);
+        mContext = Objects.requireNonNull(context);
         mLock = new Object();
         mSettingsListener = new TextClassifierSettingsListener(mContext);
     }
@@ -156,8 +157,8 @@
             @Nullable TextClassificationSessionId sessionId,
             TextSelection.Request request, ITextClassifierCallback callback)
             throws RemoteException {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(callback);
         final int userId = request.getUserId();
         validateInput(mContext, request.getCallingPackageName(), userId);
 
@@ -186,8 +187,8 @@
             @Nullable TextClassificationSessionId sessionId,
             TextClassification.Request request, ITextClassifierCallback callback)
             throws RemoteException {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(callback);
         final int userId = request.getUserId();
         validateInput(mContext, request.getCallingPackageName(), userId);
 
@@ -215,8 +216,8 @@
             @Nullable TextClassificationSessionId sessionId,
             TextLinks.Request request, ITextClassifierCallback callback)
             throws RemoteException {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(callback);
         final int userId = request.getUserId();
         validateInput(mContext, request.getCallingPackageName(), userId);
 
@@ -244,7 +245,7 @@
     public void onSelectionEvent(
             @Nullable TextClassificationSessionId sessionId, SelectionEvent event)
             throws RemoteException {
-        Preconditions.checkNotNull(event);
+        Objects.requireNonNull(event);
         final int userId = event.getUserId();
         validateInput(mContext, event.getPackageName(), userId);
 
@@ -268,7 +269,7 @@
     public void onTextClassifierEvent(
             @Nullable TextClassificationSessionId sessionId,
             TextClassifierEvent event) throws RemoteException {
-        Preconditions.checkNotNull(event);
+        Objects.requireNonNull(event);
         final String packageName = event.getEventContext() == null
                 ? null
                 : event.getEventContext().getPackageName();
@@ -299,8 +300,8 @@
             @Nullable TextClassificationSessionId sessionId,
             TextLanguage.Request request,
             ITextClassifierCallback callback) throws RemoteException {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(callback);
         final int userId = request.getUserId();
         validateInput(mContext, request.getCallingPackageName(), userId);
 
@@ -329,8 +330,8 @@
             @Nullable TextClassificationSessionId sessionId,
             ConversationActions.Request request,
             ITextClassifierCallback callback) throws RemoteException {
-        Preconditions.checkNotNull(request);
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(request);
+        Objects.requireNonNull(callback);
         final int userId = request.getUserId();
         validateInput(mContext, request.getCallingPackageName(), userId);
 
@@ -360,8 +361,8 @@
     public void onCreateTextClassificationSession(
             TextClassificationContext classificationContext, TextClassificationSessionId sessionId)
             throws RemoteException {
-        Preconditions.checkNotNull(sessionId);
-        Preconditions.checkNotNull(classificationContext);
+        Objects.requireNonNull(sessionId);
+        Objects.requireNonNull(classificationContext);
         final int userId = classificationContext.getUserId();
         validateInput(mContext, classificationContext.getPackageName(), userId);
 
@@ -391,7 +392,7 @@
     @Override
     public void onDestroyTextClassificationSession(TextClassificationSessionId sessionId)
             throws RemoteException {
-        Preconditions.checkNotNull(sessionId);
+        Objects.requireNonNull(sessionId);
 
         synchronized (mLock) {
             final int userId = mSessionUserIds.containsKey(sessionId)
@@ -514,7 +515,7 @@
                 UserState owningUser, int uid) {
             mName = name;
             mRequest =
-                    logOnFailure(Preconditions.checkNotNull(request), "handling pending request");
+                    logOnFailure(Objects.requireNonNull(request), "handling pending request");
             mOnServiceFailure =
                     logOnFailure(onServiceFailure, "notifying callback of service failure");
             mBinder = binder;
@@ -604,8 +605,8 @@
 
         private UserState(int userId, Context context, Object lock) {
             mUserId = userId;
-            mContext = Preconditions.checkNotNull(context);
-            mLock = Preconditions.checkNotNull(lock);
+            mContext = Objects.requireNonNull(context);
+            mLock = Objects.requireNonNull(lock);
         }
 
         @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 172367a..b7d6360 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.timedetector.ITimeDetectorService;
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -105,6 +106,14 @@
         mHandler.post(() -> mTimeDetectorStrategy.suggestManualTime(timeSignal));
     }
 
+    @Override
+    public void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSignal) {
+        enforceSuggestNetworkTimePermission();
+        Objects.requireNonNull(timeSignal);
+
+        mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
+    }
+
     @VisibleForTesting
     public void handleAutoTimeDetectionToggle() {
         mHandler.post(mTimeDetectorStrategy::handleAutoTimeDetectionChanged);
@@ -119,10 +128,20 @@
     }
 
     private void enforceSuggestPhoneTimePermission() {
-        mContext.enforceCallingPermission(android.Manifest.permission.SET_TIME, "set time");
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE,
+                "suggest phone time and time zone");
     }
 
     private void enforceSuggestManualTimePermission() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SET_TIME, "set time");
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE,
+                "suggest manual time and time zone");
+    }
+
+    private void enforceSuggestNetworkTimePermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.SET_TIME,
+                "set time");
     }
 }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 0a6c2e7..b4f4eca 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.util.TimestampedValue;
@@ -86,6 +87,9 @@
     /** Process the suggested manually entered time. */
     void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion);
 
+    /** Process the suggested time from network sources. */
+    void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion);
+
     /** Handle the auto-time setting being toggled on or off. */
     void handleAutoTimeDetectionChanged();
 
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index c50248d..02656ea 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.AlarmManager;
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.telephony.TelephonyManager;
@@ -32,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.timezonedetector.ArrayMapWithHistory;
+import com.android.server.timezonedetector.ReferenceWithHistory;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -56,11 +58,11 @@
     /** Each bucket is this size. All buckets are equally sized. */
     @VisibleForTesting
     static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
-    /** Phone suggestions older than this value are considered too old. */
+    /** Phone and network suggestions older than this value are considered too old to be used. */
     @VisibleForTesting
-    static final long PHONE_MAX_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
+    static final long MAX_UTC_TIME_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
 
-    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL, ORIGIN_NETWORK })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Origin {}
 
@@ -72,6 +74,10 @@
     @Origin
     private static final int ORIGIN_MANUAL = 2;
 
+    /** Used when a time value originated from a network signal. */
+    @Origin
+    private static final int ORIGIN_NETWORK = 3;
+
     /**
      * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
      * actual system clock time before a warning is logged. Used to help identify situations where
@@ -101,9 +107,13 @@
      * will have a small number of telephony devices and phoneIds are assumed to be stable.
      */
     @GuardedBy("this")
-    private ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
+    private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionByPhoneId =
             new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
+    @GuardedBy("this")
+    private final ReferenceWithHistory<NetworkTimeSuggestion> mLastNetworkSuggestion =
+            new ReferenceWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
+
     @Override
     public void initialize(@NonNull Callback callback) {
         mCallback = callback;
@@ -122,6 +132,19 @@
     }
 
     @Override
+    public synchronized void suggestNetworkTime(@NonNull NetworkTimeSuggestion timeSuggestion) {
+        if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
+            return;
+        }
+        mLastNetworkSuggestion.set(timeSuggestion);
+
+        // Now perform auto time detection. The new suggestion may be used to modify the system
+        // clock.
+        String reason = "New network time suggested. timeSuggestion=" + timeSuggestion;
+        doAutoTimeDetection(reason);
+    }
+
+    @Override
     public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
         // Empty time suggestion means that telephony network connectivity has been lost.
         // The passage of time is relentless, and we don't expect our users to use a time machine,
@@ -167,6 +190,12 @@
         ipw.increaseIndent(); // level 1
 
         ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
+        ipw.println("mCallback.isAutoTimeDetectionEnabled()="
+                + mCallback.isAutoTimeDetectionEnabled());
+        ipw.println("mCallback.elapsedRealtimeMillis()=" + mCallback.elapsedRealtimeMillis());
+        ipw.println("mCallback.systemClockMillis()=" + mCallback.systemClockMillis());
+        ipw.println("mCallback.systemClockUpdateThresholdMillis()="
+                + mCallback.systemClockUpdateThresholdMillis());
 
         ipw.println("Time change log:");
         ipw.increaseIndent(); // level 2
@@ -178,6 +207,11 @@
         mSuggestionByPhoneId.dump(ipw);
         ipw.decreaseIndent(); // level 2
 
+        ipw.println("Network suggestion history:");
+        ipw.increaseIndent(); // level 2
+        mLastNetworkSuggestion.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
         ipw.decreaseIndent(); // level 1
         ipw.flush();
     }
@@ -247,23 +281,34 @@
             return;
         }
 
+        // Android devices currently prioritize any telephony over network signals. There are
+        // carrier compliance tests that would need to be changed before we could ignore NITZ or
+        // prefer NTP generally. This check is cheap on devices without phone hardware.
         PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
-
-        // Work out what to do with the best suggestion.
-        if (bestPhoneSuggestion == null) {
-            // There is no good phone suggestion.
-            if (DBG) {
-                Slog.d(LOG_TAG, "Could not determine time: No best phone suggestion."
-                        + " detectionReason=" + detectionReason);
-            }
+        if (bestPhoneSuggestion != null) {
+            final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
+            String cause = "Found good phone suggestion."
+                    + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                    + ", detectionReason=" + detectionReason;
+            setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
             return;
         }
 
-        final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
-        String cause = "Found good suggestion."
-                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
-                + ", detectionReason=" + detectionReason;
-        setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
+        // There is no good phone suggestion, try network.
+        NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion();
+        if (networkSuggestion != null) {
+            final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime();
+            String cause = "Found good network suggestion."
+                    + ", networkSuggestion=" + networkSuggestion
+                    + ", detectionReason=" + detectionReason;
+            setSystemClockIfRequired(ORIGIN_NETWORK, newUtcTime, cause);
+            return;
+        }
+
+        if (DBG) {
+            Slog.d(LOG_TAG, "Could not determine time: No best phone or network suggestion."
+                    + " detectionReason=" + detectionReason);
+        }
     }
 
     @GuardedBy("this")
@@ -342,37 +387,50 @@
 
     private static int scorePhoneSuggestion(
             long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
-        // The score is based on the age since receipt. Suggestions are bucketed so two
-        // suggestions in the same bucket from different phoneIds are scored the same.
+
+        // Validate first.
         TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
-        long referenceTimeMillis = utcTime.getReferenceTimeMillis();
-        if (referenceTimeMillis > elapsedRealtimeMillis) {
-            // Future times are ignored. They imply the reference time was wrong, or the elapsed
-            // realtime clock has gone backwards, neither of which are supportable situations.
-            Slog.w(LOG_TAG, "Existing suggestion found to be in the future. "
+        if (!validateSuggestionUtcTime(elapsedRealtimeMillis, utcTime)) {
+            Slog.w(LOG_TAG, "Existing suggestion found to be invalid "
                     + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
                     + ", timeSuggestion=" + timeSuggestion);
             return PHONE_INVALID_SCORE;
         }
 
-        long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
+        // The score is based on the age since receipt. Suggestions are bucketed so two
+        // suggestions in the same bucket from different phoneIds are scored the same.
+        long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis();
 
-        // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
-        // predictable, the accuracy of the reference time clock may be poor over long periods which
-        // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
-        // made and never replaced, it could also mean that the time detection code remains
-        // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
-        if (ageMillis > PHONE_MAX_AGE_MILLIS) {
+        // Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT.
+        int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
+        if (bucketIndex >= PHONE_BUCKET_COUNT) {
             return PHONE_INVALID_SCORE;
         }
 
-        // Turn the age into a discrete value: 0 <= bucketIndex < MAX_AGE_HOURS.
-        int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
-
         // We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
         return PHONE_BUCKET_COUNT - bucketIndex;
     }
 
+    /** Returns the latest, valid, network suggestion. Returns {@code null} if there isn't one. */
+    @GuardedBy("this")
+    @Nullable
+    private NetworkTimeSuggestion findLatestValidNetworkSuggestion() {
+        NetworkTimeSuggestion networkSuggestion = mLastNetworkSuggestion.get();
+        if (networkSuggestion == null) {
+            // No network suggestions received. This is normal if there's no connectivity.
+            return null;
+        }
+
+        TimestampedValue<Long> utcTime = networkSuggestion.getUtcTime();
+        long elapsedRealTimeMillis = mCallback.elapsedRealtimeMillis();
+        if (!validateSuggestionUtcTime(elapsedRealTimeMillis, utcTime)) {
+            // The latest suggestion is not valid, usually due to its age.
+            return null;
+        }
+
+        return networkSuggestion;
+    }
+
     @GuardedBy("this")
     private void setSystemClockIfRequired(
             @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
@@ -409,7 +467,7 @@
     }
 
     private static boolean isOriginAutomatic(@Origin int origin) {
-        return origin == ORIGIN_PHONE;
+        return origin != ORIGIN_MANUAL;
     }
 
     @GuardedBy("this")
@@ -501,6 +559,16 @@
     }
 
     /**
+     * Returns the latest valid network suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
+     */
+    @VisibleForTesting
+    @Nullable
+    public NetworkTimeSuggestion findLatestValidNetworkSuggestionForTests() {
+        return findLatestValidNetworkSuggestion();
+    }
+
+    /**
      * A method used to inspect state during tests. Not intended for general use.
      */
     @VisibleForTesting
@@ -508,4 +576,32 @@
     public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
         return mSuggestionByPhoneId.get(phoneId);
     }
+
+    /**
+     * A method used to inspect state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
+    @Nullable
+    public NetworkTimeSuggestion getLatestNetworkSuggestion() {
+        return mLastNetworkSuggestion.get();
+    }
+
+    private static boolean validateSuggestionUtcTime(
+            long elapsedRealtimeMillis, TimestampedValue<Long> utcTime) {
+        long referenceTimeMillis = utcTime.getReferenceTimeMillis();
+        if (referenceTimeMillis > elapsedRealtimeMillis) {
+            // Future reference times are ignored. They imply the reference time was wrong, or the
+            // elapsed realtime clock used to derive it has gone backwards, neither of which are
+            // supportable situations.
+            return false;
+        }
+
+        // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
+        // predictable, the accuracy of the reference time clock may be poor over long periods which
+        // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
+        // made and never replaced, it could also mean that the time detection code remains
+        // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
+        long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
+        return ageMillis <= MAX_UTC_TIME_AGE_MILLIS;
+    }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 18ed51a..5b58199 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1814,8 +1814,8 @@
         }
 
         @Override
-        public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info, int device)
-                throws RemoteException {
+        public ParcelFileDescriptor openDvbDevice(DvbDeviceInfo info,
+                @TvInputManager.DvbDeviceType int deviceType)  throws RemoteException {
             if (mContext.checkCallingPermission(android.Manifest.permission.DVB_DEVICE)
                     != PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException("Requires DVB_DEVICE permission");
@@ -1852,7 +1852,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 String deviceFileName;
-                switch (device) {
+                switch (deviceType) {
                     case TvInputManager.DVB_DEVICE_DEMUX:
                         deviceFileName = String.format(dvbDeviceFound
                                 ? "/dev/dvb/adapter%d/demux%d" : "/dev/dvb%d.demux%d",
@@ -1869,14 +1869,14 @@
                                 info.getAdapterId(), info.getDeviceId());
                         break;
                     default:
-                        throw new IllegalArgumentException("Invalid DVB device: " + device);
+                        throw new IllegalArgumentException("Invalid DVB device: " + deviceType);
                 }
                 try {
                     // The DVB frontend device only needs to be opened in read/write mode, which
                     // allows performing tuning operations. The DVB demux and DVR device are enough
                     // to be opened in read only mode.
                     return ParcelFileDescriptor.open(new File(deviceFileName),
-                            TvInputManager.DVB_DEVICE_FRONTEND == device
+                            TvInputManager.DVB_DEVICE_FRONTEND == deviceType
                                     ? ParcelFileDescriptor.MODE_READ_WRITE
                                     : ParcelFileDescriptor.MODE_READ_ONLY);
                 } catch (FileNotFoundException e) {
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index 04839e1..e3b7c0a 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -103,6 +103,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 
 /** Manages uri grants. */
 public class UriGrantsManagerService extends IUriGrantsManager.Stub {
@@ -215,7 +216,7 @@
     public ParceledListSlice<android.content.UriPermission> getUriPermissions(
             String packageName, boolean incoming, boolean persistedOnly) {
         enforceNotIsolatedCaller("getUriPermissions");
-        Preconditions.checkNotNull(packageName, "packageName");
+        Objects.requireNonNull(packageName, "packageName");
 
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
diff --git a/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
new file mode 100644
index 0000000..7fe4bf8
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/CountQuotaTracker.java
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils.quota;
+
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.server.utils.quota.Uptc.string;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.LongArrayQueue;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.quota.CountQuotaTrackerProto;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Class that tracks whether an app has exceeded its defined count quota.
+ *
+ * Quotas are applied per userId-package-tag combination (UPTC). Tags can be null.
+ *
+ * This tracker tracks the count of instantaneous events.
+ *
+ * Limits are applied according to the category the UPTC is placed in. If a UPTC reaches its limit,
+ * it will be considered out of quota until it is below that limit again. A {@link Category} is a
+ * basic construct to apply different limits to different groups of UPTCs. For example, standby
+ * buckets can be a set of categories, or foreground & background could be two categories. If every
+ * UPTC should have the same limits applied, then only one category is needed
+ * ({@see Category.SINGLE_CATEGORY}).
+ *
+ * Note: all limits are enforced per category unless explicitly stated otherwise.
+ *
+ * Test: atest com.android.server.utils.quota.CountQuotaTrackerTest
+ *
+ * @hide
+ */
+public class CountQuotaTracker extends QuotaTracker {
+    private static final String TAG = CountQuotaTracker.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
+    private static final String ALARM_TAG_CLEANUP = "*" + TAG + ".cleanup*";
+
+    @VisibleForTesting
+    static class ExecutionStats {
+        /**
+         * The time after which this record should be considered invalid (out of date), in the
+         * elapsed realtime timebase.
+         */
+        public long expirationTimeElapsed;
+
+        /** The window size that's used when counting the number of events. */
+        public long windowSizeMs;
+        /** The maximum number of events allowed within the window size. */
+        public int countLimit;
+
+        /** The total number of events that occurred in the window. */
+        public int countInWindow;
+
+        /**
+         * The time after which the app will be under the category quota again. This is only valid
+         * if {@link #countInWindow} >= {@link #countLimit}.
+         */
+        public long inQuotaTimeElapsed;
+
+        @Override
+        public String toString() {
+            return "expirationTime=" + expirationTimeElapsed + ", "
+                    + "windowSizeMs=" + windowSizeMs + ", "
+                    + "countLimit=" + countLimit + ", "
+                    + "countInWindow=" + countInWindow + ", "
+                    + "inQuotaTime=" + inQuotaTimeElapsed;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof ExecutionStats) {
+                ExecutionStats other = (ExecutionStats) obj;
+                return this.expirationTimeElapsed == other.expirationTimeElapsed
+                        && this.windowSizeMs == other.windowSizeMs
+                        && this.countLimit == other.countLimit
+                        && this.countInWindow == other.countInWindow
+                        && this.inQuotaTimeElapsed == other.inQuotaTimeElapsed;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 0;
+            result = 31 * result + Long.hashCode(expirationTimeElapsed);
+            result = 31 * result + Long.hashCode(windowSizeMs);
+            result = 31 * result + countLimit;
+            result = 31 * result + countInWindow;
+            result = 31 * result + Long.hashCode(inQuotaTimeElapsed);
+            return result;
+        }
+    }
+
+    /** List of times of all instantaneous events for a UPTC, in chronological order. */
+    // TODO(146148168): introduce a bucketized mode that's more efficient but less accurate
+    @GuardedBy("mLock")
+    private final UptcMap<LongArrayQueue> mEventTimes = new UptcMap<>();
+
+    /** Cached calculation results for each app. */
+    @GuardedBy("mLock")
+    private final UptcMap<ExecutionStats> mExecutionStatsCache = new UptcMap<>();
+
+    private final Handler mHandler;
+
+    @GuardedBy("mLock")
+    private long mNextCleanupTimeElapsed = 0;
+    @GuardedBy("mLock")
+    private final AlarmManager.OnAlarmListener mEventCleanupAlarmListener = () ->
+            CountQuotaTracker.this.mHandler.obtainMessage(MSG_CLEAN_UP_EVENTS).sendToTarget();
+
+    /** The rolling window size for each Category's count limit. */
+    @GuardedBy("mLock")
+    private final ArrayMap<Category, Long> mCategoryCountWindowSizesMs = new ArrayMap<>();
+
+    /**
+     * The maximum count for each Category. For each max value count in the map, the app will
+     * not be allowed any more events within the latest time interval of its rolling window size.
+     *
+     * @see #mCategoryCountWindowSizesMs
+     */
+    @GuardedBy("mLock")
+    private final ArrayMap<Category, Integer> mMaxCategoryCounts = new ArrayMap<>();
+
+    /** The longest period a registered category applies to. */
+    @GuardedBy("mLock")
+    private long mMaxPeriodMs = 0;
+
+    /** Drop any old events. */
+    private static final int MSG_CLEAN_UP_EVENTS = 1;
+
+    public CountQuotaTracker(@NonNull Context context, @NonNull Categorizer categorizer) {
+        this(context, categorizer, new Injector());
+    }
+
+    @VisibleForTesting
+    CountQuotaTracker(@NonNull Context context, @NonNull Categorizer categorizer,
+            Injector injector) {
+        super(context, categorizer, injector);
+
+        mHandler = new CqtHandler(context.getMainLooper());
+    }
+
+    // Exposed API to users.
+
+    /**
+     * Record that an instantaneous event happened.
+     *
+     * @return true if the UPTC is within quota, false otherwise.
+     */
+    public boolean noteEvent(int userId, @NonNull String packageName, @Nullable String tag) {
+        synchronized (mLock) {
+            if (!isEnabledLocked() || isQuotaFreeLocked(userId, packageName)) {
+                return true;
+            }
+            final long nowElapsed = mInjector.getElapsedRealtime();
+
+            final LongArrayQueue times = mEventTimes
+                    .getOrCreate(userId, packageName, tag, mCreateLongArrayQueue);
+            times.addLast(nowElapsed);
+            final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, tag);
+            stats.countInWindow++;
+            stats.expirationTimeElapsed = Math.min(stats.expirationTimeElapsed,
+                    nowElapsed + stats.windowSizeMs);
+            if (stats.countInWindow == stats.countLimit) {
+                final long windowEdgeElapsed = nowElapsed - stats.windowSizeMs;
+                while (times.size() > 0 && times.peekFirst() < windowEdgeElapsed) {
+                    times.removeFirst();
+                }
+                stats.inQuotaTimeElapsed = times.peekFirst() + stats.windowSizeMs;
+                postQuotaStatusChanged(userId, packageName, tag);
+            } else if (stats.countLimit > 9
+                    && stats.countInWindow == stats.countLimit * 4 / 5) {
+                // TODO: log high watermark to statsd
+                Slog.w(TAG, string(userId, packageName, tag)
+                        + " has reached 80% of it's count limit of " + stats.countLimit);
+            }
+            maybeScheduleCleanupAlarmLocked();
+            return isWithinQuotaLocked(stats);
+        }
+    }
+
+    /**
+     * Set count limit over a rolling time window for the specified category.
+     *
+     * @param category     The category these limits apply to.
+     * @param limit        The maximum event count an app can have in the rolling window. Must be
+     *                     nonnegative.
+     * @param timeWindowMs The rolling time window (in milliseconds) to use when checking quota
+     *                     usage. Must be at least {@value #MIN_WINDOW_SIZE_MS} and no longer than
+     *                     {@value #MAX_WINDOW_SIZE_MS}
+     */
+    public void setCountLimit(@NonNull Category category, int limit, long timeWindowMs) {
+        if (limit < 0 || timeWindowMs < 0) {
+            throw new IllegalArgumentException("Limit and window size must be nonnegative.");
+        }
+        synchronized (mLock) {
+            final Integer oldLimit = mMaxCategoryCounts.put(category, limit);
+            final long newWindowSizeMs = Math.max(MIN_WINDOW_SIZE_MS,
+                    Math.min(timeWindowMs, MAX_WINDOW_SIZE_MS));
+            final Long oldWindowSizeMs = mCategoryCountWindowSizesMs.put(category, newWindowSizeMs);
+            if (oldLimit != null && oldWindowSizeMs != null
+                    && oldLimit == limit && oldWindowSizeMs == newWindowSizeMs) {
+                // No change.
+                return;
+            }
+            mDeleteOldEventTimesFunctor.updateMaxPeriod();
+            mMaxPeriodMs = mDeleteOldEventTimesFunctor.mMaxPeriodMs;
+            invalidateAllExecutionStatsLocked();
+        }
+        scheduleQuotaCheck();
+    }
+
+    /**
+     * Gets the count limit for the specified category.
+     */
+    public int getLimit(@NonNull Category category) {
+        synchronized (mLock) {
+            final Integer limit = mMaxCategoryCounts.get(category);
+            if (limit == null) {
+                throw new IllegalArgumentException("Limit for " + category + " not defined");
+            }
+            return limit;
+        }
+    }
+
+    /**
+     * Gets the count time window for the specified category.
+     */
+    public long getWindowSizeMs(@NonNull Category category) {
+        synchronized (mLock) {
+            final Long limitMs = mCategoryCountWindowSizesMs.get(category);
+            if (limitMs == null) {
+                throw new IllegalArgumentException("Limit for " + category + " not defined");
+            }
+            return limitMs;
+        }
+    }
+
+    // Internal implementation.
+
+    @Override
+    @GuardedBy("mLock")
+    void dropEverythingLocked() {
+        mExecutionStatsCache.clear();
+        mEventTimes.clear();
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    @NonNull
+    Handler getHandler() {
+        return mHandler;
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    long getInQuotaTimeElapsedLocked(final int userId, @NonNull final String packageName,
+            @Nullable final String tag) {
+        return getExecutionStatsLocked(userId, packageName, tag).inQuotaTimeElapsed;
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void handleRemovedAppLocked(String packageName, int uid) {
+        if (packageName == null) {
+            Slog.wtf(TAG, "Told app removed but given null package name.");
+            return;
+        }
+        final int userId = UserHandle.getUserId(uid);
+
+        mEventTimes.delete(userId, packageName);
+        mExecutionStatsCache.delete(userId, packageName);
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void handleRemovedUserLocked(int userId) {
+        mEventTimes.delete(userId);
+        mExecutionStatsCache.delete(userId);
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
+            @Nullable final String tag) {
+        if (!isEnabledLocked()) return true;
+
+        // Quota constraint is not enforced when quota is free.
+        if (isQuotaFreeLocked(userId, packageName)) {
+            return true;
+        }
+
+        return isWithinQuotaLocked(getExecutionStatsLocked(userId, packageName, tag));
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void maybeUpdateAllQuotaStatusLocked() {
+        final UptcMap<Boolean> doneMap = new UptcMap<>();
+        mEventTimes.forEach((userId, packageName, tag, events) -> {
+            if (!doneMap.contains(userId, packageName, tag)) {
+                maybeUpdateStatusForUptcLocked(userId, packageName, tag);
+                doneMap.add(userId, packageName, tag, Boolean.TRUE);
+            }
+        });
+
+    }
+
+    @Override
+    void maybeUpdateQuotaStatus(final int userId, @NonNull final String packageName,
+            @Nullable final String tag) {
+        synchronized (mLock) {
+            maybeUpdateStatusForUptcLocked(userId, packageName, tag);
+        }
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void onQuotaFreeChangedLocked(boolean isFree) {
+        // Nothing to do here.
+    }
+
+    @Override
+    @GuardedBy("mLock")
+    void onQuotaFreeChangedLocked(int userId, @NonNull String packageName, boolean isFree) {
+        maybeUpdateStatusForPkgLocked(userId, packageName);
+    }
+
+    @GuardedBy("mLock")
+    private boolean isWithinQuotaLocked(@NonNull final ExecutionStats stats) {
+        return isUnderCountQuotaLocked(stats);
+    }
+
+    @GuardedBy("mLock")
+    private boolean isUnderCountQuotaLocked(@NonNull ExecutionStats stats) {
+        return stats.countInWindow < stats.countLimit;
+    }
+
+    /** Returns the execution stats of the app in the most recent window. */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    @NonNull
+    ExecutionStats getExecutionStatsLocked(final int userId, @NonNull final String packageName,
+            @Nullable final String tag) {
+        return getExecutionStatsLocked(userId, packageName, tag, true);
+    }
+
+    @GuardedBy("mLock")
+    @NonNull
+    private ExecutionStats getExecutionStatsLocked(final int userId,
+            @NonNull final String packageName, @Nullable String tag,
+            final boolean refreshStatsIfOld) {
+        final ExecutionStats stats =
+                mExecutionStatsCache.getOrCreate(userId, packageName, tag, mCreateExecutionStats);
+        if (refreshStatsIfOld) {
+            final Category category = mCategorizer.getCategory(userId, packageName, tag);
+            final long countWindowSizeMs = mCategoryCountWindowSizesMs.getOrDefault(category,
+                    Long.MAX_VALUE);
+            final int countLimit = mMaxCategoryCounts.getOrDefault(category, Integer.MAX_VALUE);
+            if (stats.expirationTimeElapsed <= mInjector.getElapsedRealtime()
+                    || stats.windowSizeMs != countWindowSizeMs
+                    || stats.countLimit != countLimit) {
+                // The stats are no longer valid.
+                stats.windowSizeMs = countWindowSizeMs;
+                stats.countLimit = countLimit;
+                updateExecutionStatsLocked(userId, packageName, tag, stats);
+            }
+        }
+
+        return stats;
+    }
+
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    void updateExecutionStatsLocked(final int userId, @NonNull final String packageName,
+            @Nullable final String tag, @NonNull ExecutionStats stats) {
+        stats.countInWindow = 0;
+        stats.inQuotaTimeElapsed = 0;
+
+        // This can be used to determine when an app will have enough quota to transition from
+        // out-of-quota to in-quota.
+        final long nowElapsed = mInjector.getElapsedRealtime();
+        stats.expirationTimeElapsed = nowElapsed + mMaxPeriodMs;
+
+        final LongArrayQueue events = mEventTimes.get(userId, packageName, tag);
+        if (events == null) {
+            return;
+        }
+
+        // The minimum time between the start time and the beginning of the events that were
+        // looked at --> how much time the stats will be valid for.
+        long emptyTimeMs = Long.MAX_VALUE - nowElapsed;
+
+        final long eventStartWindowElapsed = nowElapsed - stats.windowSizeMs;
+        for (int i = events.size() - 1; i >= 0; --i) {
+            final long eventTimeElapsed = events.get(i);
+            if (eventTimeElapsed < eventStartWindowElapsed) {
+                // This event happened before the window. No point in going any further.
+                break;
+            }
+            stats.countInWindow++;
+            emptyTimeMs = Math.min(emptyTimeMs, eventTimeElapsed - eventStartWindowElapsed);
+
+            if (stats.countInWindow >= stats.countLimit) {
+                stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
+                        eventTimeElapsed + stats.windowSizeMs);
+            }
+        }
+
+        stats.expirationTimeElapsed = nowElapsed + emptyTimeMs;
+    }
+
+    /** Invalidate ExecutionStats for all apps. */
+    @GuardedBy("mLock")
+    private void invalidateAllExecutionStatsLocked() {
+        final long nowElapsed = mInjector.getElapsedRealtime();
+        mExecutionStatsCache.forEach((appStats) -> {
+            if (appStats != null) {
+                appStats.expirationTimeElapsed = nowElapsed;
+            }
+        });
+    }
+
+    @GuardedBy("mLock")
+    private void invalidateAllExecutionStatsLocked(final int userId,
+            @NonNull final String packageName) {
+        final ArrayMap<String, ExecutionStats> appStats =
+                mExecutionStatsCache.get(userId, packageName);
+        if (appStats != null) {
+            final long nowElapsed = mInjector.getElapsedRealtime();
+            final int numStats = appStats.size();
+            for (int i = 0; i < numStats; ++i) {
+                final ExecutionStats stats = appStats.valueAt(i);
+                if (stats != null) {
+                    stats.expirationTimeElapsed = nowElapsed;
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void invalidateExecutionStatsLocked(final int userId, @NonNull final String packageName,
+            @Nullable String tag) {
+        final ExecutionStats stats = mExecutionStatsCache.get(userId, packageName, tag);
+        if (stats != null) {
+            stats.expirationTimeElapsed = mInjector.getElapsedRealtime();
+        }
+    }
+
+    private static final class EarliestEventTimeFunctor implements Consumer<LongArrayQueue> {
+        long earliestTimeElapsed = Long.MAX_VALUE;
+
+        @Override
+        public void accept(LongArrayQueue events) {
+            if (events != null && events.size() > 0) {
+                earliestTimeElapsed = Math.min(earliestTimeElapsed, events.get(0));
+            }
+        }
+
+        void reset() {
+            earliestTimeElapsed = Long.MAX_VALUE;
+        }
+    }
+
+    private final EarliestEventTimeFunctor mEarliestEventTimeFunctor =
+            new EarliestEventTimeFunctor();
+
+    /** Schedule a cleanup alarm if necessary and there isn't already one scheduled. */
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    void maybeScheduleCleanupAlarmLocked() {
+        if (mNextCleanupTimeElapsed > mInjector.getElapsedRealtime()) {
+            // There's already an alarm scheduled. Just stick with that one. There's no way we'll
+            // end up scheduling an earlier alarm.
+            if (DEBUG) {
+                Slog.v(TAG, "Not scheduling cleanup since there's already one at "
+                        + mNextCleanupTimeElapsed + " (in " + (mNextCleanupTimeElapsed
+                        - mInjector.getElapsedRealtime()) + "ms)");
+            }
+            return;
+        }
+
+        mEarliestEventTimeFunctor.reset();
+        mEventTimes.forEach(mEarliestEventTimeFunctor);
+        final long earliestEndElapsed = mEarliestEventTimeFunctor.earliestTimeElapsed;
+        if (earliestEndElapsed == Long.MAX_VALUE) {
+            // Couldn't find a good time to clean up. Maybe this was called after we deleted all
+            // events.
+            if (DEBUG) {
+                Slog.d(TAG, "Didn't find a time to schedule cleanup");
+            }
+            return;
+        }
+
+        // Need to keep events for all apps up to the max period, regardless of their current
+        // category.
+        long nextCleanupElapsed = earliestEndElapsed + mMaxPeriodMs;
+        if (nextCleanupElapsed - mNextCleanupTimeElapsed <= 10 * MINUTE_IN_MILLIS) {
+            // No need to clean up too often. Delay the alarm if the next cleanup would be too soon
+            // after it.
+            nextCleanupElapsed += 10 * MINUTE_IN_MILLIS;
+        }
+        mNextCleanupTimeElapsed = nextCleanupElapsed;
+        scheduleAlarm(AlarmManager.ELAPSED_REALTIME, nextCleanupElapsed, ALARM_TAG_CLEANUP,
+                mEventCleanupAlarmListener);
+        if (DEBUG) {
+            Slog.d(TAG, "Scheduled next cleanup for " + mNextCleanupTimeElapsed);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private boolean maybeUpdateStatusForPkgLocked(final int userId,
+            @NonNull final String packageName) {
+        final UptcMap<Boolean> done = new UptcMap<>();
+
+        if (!mEventTimes.contains(userId, packageName)) {
+            return false;
+        }
+        final ArrayMap<String, LongArrayQueue> events = mEventTimes.get(userId, packageName);
+        if (events == null) {
+            Slog.wtf(TAG,
+                    "Events map was null even though mEventTimes said it contained "
+                            + string(userId, packageName, null));
+            return false;
+        }
+
+        // Lambdas can't interact with non-final outer variables.
+        final boolean[] changed = {false};
+        events.forEach((tag, eventList) -> {
+            if (!done.contains(userId, packageName, tag)) {
+                changed[0] |= maybeUpdateStatusForUptcLocked(userId, packageName, tag);
+                done.add(userId, packageName, tag, Boolean.TRUE);
+            }
+        });
+
+        return changed[0];
+    }
+
+    /**
+     * Posts that the quota status for the UPTC has changed if it has changed. Avoid calling if
+     * there are no {@link QuotaChangeListener}s registered as the work done will be useless.
+     *
+     * @return true if the in/out quota status changed
+     */
+    @GuardedBy("mLock")
+    private boolean maybeUpdateStatusForUptcLocked(final int userId,
+            @NonNull final String packageName, @Nullable final String tag) {
+        final boolean oldInQuota = isWithinQuotaLocked(
+                getExecutionStatsLocked(userId, packageName, tag, false));
+
+        final boolean newInQuota;
+        if (!isEnabledLocked() || isQuotaFreeLocked(userId, packageName)) {
+            newInQuota = true;
+        } else {
+            newInQuota = isWithinQuotaLocked(
+                    getExecutionStatsLocked(userId, packageName, tag, true));
+        }
+
+        if (!newInQuota) {
+            maybeScheduleStartAlarmLocked(userId, packageName, tag);
+        } else {
+            cancelScheduledStartAlarmLocked(userId, packageName, tag);
+        }
+
+        if (oldInQuota != newInQuota) {
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "Quota status changed from " + oldInQuota + " to " + newInQuota + " for "
+                                + string(userId, packageName, tag));
+            }
+            postQuotaStatusChanged(userId, packageName, tag);
+            return true;
+        }
+
+        return false;
+    }
+
+    private final class DeleteEventTimesFunctor implements Consumer<LongArrayQueue> {
+        private long mMaxPeriodMs;
+
+        @Override
+        public void accept(LongArrayQueue times) {
+            if (times != null) {
+                // Remove everything older than mMaxPeriodMs time ago.
+                while (times.size() > 0
+                        && times.peekFirst() <= mInjector.getElapsedRealtime() - mMaxPeriodMs) {
+                    times.removeFirst();
+                }
+            }
+        }
+
+        private void updateMaxPeriod() {
+            long maxPeriodMs = 0;
+            for (int i = mCategoryCountWindowSizesMs.size() - 1; i >= 0; --i) {
+                maxPeriodMs = Long.max(maxPeriodMs, mCategoryCountWindowSizesMs.valueAt(i));
+            }
+            mMaxPeriodMs = maxPeriodMs;
+        }
+    }
+
+    private final DeleteEventTimesFunctor mDeleteOldEventTimesFunctor =
+            new DeleteEventTimesFunctor();
+
+    @GuardedBy("mLock")
+    @VisibleForTesting
+    void deleteObsoleteEventsLocked() {
+        mEventTimes.forEach(mDeleteOldEventTimesFunctor);
+    }
+
+    private class CqtHandler extends Handler {
+        CqtHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            synchronized (mLock) {
+                switch (msg.what) {
+                    case MSG_CLEAN_UP_EVENTS: {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Cleaning up events.");
+                        }
+                        deleteObsoleteEventsLocked();
+                        maybeScheduleCleanupAlarmLocked();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    private Function<Void, LongArrayQueue> mCreateLongArrayQueue = aVoid -> new LongArrayQueue();
+    private Function<Void, ExecutionStats> mCreateExecutionStats = aVoid -> new ExecutionStats();
+
+    //////////////////////// TESTING HELPERS /////////////////////////////
+
+    @VisibleForTesting
+    @Nullable
+    LongArrayQueue getEvents(int userId, String packageName, String tag) {
+        return mEventTimes.get(userId, packageName, tag);
+    }
+
+    //////////////////////////// DATA DUMP //////////////////////////////
+
+    /** Dump state in text format. */
+    public void dump(final IndentingPrintWriter pw) {
+        pw.print(TAG);
+        pw.println(":");
+        pw.increaseIndent();
+
+        synchronized (mLock) {
+            super.dump(pw);
+            pw.println();
+
+            pw.println("Instantaneous events:");
+            pw.increaseIndent();
+            mEventTimes.forEach((userId, pkgName, tag, events) -> {
+                if (events.size() > 0) {
+                    pw.print(string(userId, pkgName, tag));
+                    pw.println(":");
+                    pw.increaseIndent();
+                    pw.print(events.get(0));
+                    for (int i = 1; i < events.size(); ++i) {
+                        pw.print(", ");
+                        pw.print(events.get(i));
+                    }
+                    pw.decreaseIndent();
+                    pw.println();
+                }
+            });
+            pw.decreaseIndent();
+
+            pw.println();
+            pw.println("Cached execution stats:");
+            pw.increaseIndent();
+            mExecutionStatsCache.forEach((userId, pkgName, tag, stats) -> {
+                if (stats != null) {
+                    pw.print(string(userId, pkgName, tag));
+                    pw.println(":");
+                    pw.increaseIndent();
+                    pw.println(stats);
+                    pw.decreaseIndent();
+                }
+            });
+            pw.decreaseIndent();
+
+            pw.println();
+            pw.println("Limits:");
+            pw.increaseIndent();
+            final int numCategories = mCategoryCountWindowSizesMs.size();
+            for (int i = 0; i < numCategories; ++i) {
+                final Category category = mCategoryCountWindowSizesMs.keyAt(i);
+                pw.print(category);
+                pw.print(": ");
+                pw.print(mMaxCategoryCounts.get(category));
+                pw.print(" events in ");
+                pw.println(TimeUtils.formatDuration(mCategoryCountWindowSizesMs.get(category)));
+            }
+            pw.decreaseIndent();
+        }
+        pw.decreaseIndent();
+    }
+
+    /**
+     * Dump state to proto.
+     *
+     * @param proto   The ProtoOutputStream to write to.
+     * @param fieldId The field ID of the {@link CountQuotaTrackerProto}.
+     */
+    public void dump(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        synchronized (mLock) {
+            super.dump(proto, CountQuotaTrackerProto.BASE_QUOTA_DATA);
+
+            for (int i = 0; i < mCategoryCountWindowSizesMs.size(); ++i) {
+                final Category category = mCategoryCountWindowSizesMs.keyAt(i);
+                final long clToken = proto.start(CountQuotaTrackerProto.COUNT_LIMIT);
+                category.dumpDebug(proto, CountQuotaTrackerProto.CountLimit.CATEGORY);
+                proto.write(CountQuotaTrackerProto.CountLimit.LIMIT,
+                        mMaxCategoryCounts.get(category));
+                proto.write(CountQuotaTrackerProto.CountLimit.WINDOW_SIZE_MS,
+                        mCategoryCountWindowSizesMs.get(category));
+                proto.end(clToken);
+            }
+
+            mExecutionStatsCache.forEach((userId, pkgName, tag, stats) -> {
+                final boolean isQuotaFree = isIndividualQuotaFreeLocked(userId, pkgName);
+
+                final long usToken = proto.start(CountQuotaTrackerProto.UPTC_STATS);
+
+                (new Uptc(userId, pkgName, tag))
+                        .dumpDebug(proto, CountQuotaTrackerProto.UptcStats.UPTC);
+
+                proto.write(CountQuotaTrackerProto.UptcStats.IS_QUOTA_FREE, isQuotaFree);
+
+                final LongArrayQueue events = mEventTimes.get(userId, pkgName, tag);
+                if (events != null) {
+                    for (int j = events.size() - 1; j >= 0; --j) {
+                        final long eToken = proto.start(CountQuotaTrackerProto.UptcStats.EVENTS);
+                        proto.write(CountQuotaTrackerProto.Event.TIMESTAMP_ELAPSED, events.get(j));
+                        proto.end(eToken);
+                    }
+                }
+
+                final long statsToken = proto.start(
+                        CountQuotaTrackerProto.UptcStats.EXECUTION_STATS);
+                proto.write(
+                        CountQuotaTrackerProto.ExecutionStats.EXPIRATION_TIME_ELAPSED,
+                        stats.expirationTimeElapsed);
+                proto.write(
+                        CountQuotaTrackerProto.ExecutionStats.WINDOW_SIZE_MS,
+                        stats.windowSizeMs);
+                proto.write(CountQuotaTrackerProto.ExecutionStats.COUNT_LIMIT, stats.countLimit);
+                proto.write(
+                        CountQuotaTrackerProto.ExecutionStats.COUNT_IN_WINDOW,
+                        stats.countInWindow);
+                proto.write(
+                        CountQuotaTrackerProto.ExecutionStats.IN_QUOTA_TIME_ELAPSED,
+                        stats.inQuotaTimeElapsed);
+                proto.end(statsToken);
+
+                proto.end(usToken);
+            });
+
+            proto.end(token);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/utils/quota/UptcMap.java b/services/core/java/com/android/server/utils/quota/UptcMap.java
index 7b49913..a3d6ee5 100644
--- a/services/core/java/com/android/server/utils/quota/UptcMap.java
+++ b/services/core/java/com/android/server/utils/quota/UptcMap.java
@@ -22,6 +22,7 @@
 import android.util.SparseArrayMap;
 
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /**
  * A SparseArrayMap of ArrayMaps, which is suitable for holding userId-packageName-tag combination
@@ -95,36 +96,36 @@
     }
 
     /**
-     * Returns the index for which {@link #getUserIdAtIndex(int)} would return the specified userId,
-     * or a negative number if the specified userId is not mapped.
+     * Returns the saved object for the given UPTC. If there was no saved object, it will create a
+     * new object using creator, insert it, and return it.
      */
-    public int indexOfUserId(int userId) {
-        return mData.indexOfKey(userId);
-    }
-
-    /**
-     * Returns the index for which {@link #getPackageNameAtIndex(int, int)} would return the
-     * specified userId, or a negative number if the specified userId and packageName are not mapped
-     * together.
-     */
-    public int indexOfUserIdAndPackage(int userId, @NonNull String packageName) {
-        return mData.indexOfKey(userId, packageName);
+    @Nullable
+    public T getOrCreate(int userId, @NonNull String packageName, @Nullable String tag,
+            Function<Void, T> creator) {
+        final ArrayMap<String, T> data = mData.get(userId, packageName);
+        if (data == null || !data.containsKey(tag)) {
+            // We've never inserted data for this combination before. Create a new object.
+            final T val = creator.apply(null);
+            add(userId, packageName, tag, val);
+            return val;
+        }
+        return data.get(tag);
     }
 
     /** Returns the userId at the given index. */
-    public int getUserIdAtIndex(int index) {
+    private int getUserIdAtIndex(int index) {
         return mData.keyAt(index);
     }
 
     /** Returns the package name at the given index. */
     @NonNull
-    public String getPackageNameAtIndex(int userIndex, int packageIndex) {
+    private String getPackageNameAtIndex(int userIndex, int packageIndex) {
         return mData.keyAt(userIndex, packageIndex);
     }
 
     /** Returns the tag at the given index. */
     @NonNull
-    public String getTagAtIndex(int userIndex, int packageIndex, int tagIndex) {
+    private String getTagAtIndex(int userIndex, int packageIndex, int tagIndex) {
         // This structure never inserts a null ArrayMap, so if the indices are valid, valueAt()
         // won't return null.
         return mData.valueAt(userIndex, packageIndex).keyAt(tagIndex);
@@ -160,4 +161,26 @@
             }
         });
     }
+
+    public void forEach(UptcDataConsumer<T> consumer) {
+        final int uCount = userCount();
+        for (int u = 0; u < uCount; ++u) {
+            final int userId = getUserIdAtIndex(u);
+
+            final int pkgCount = packageCountForUser(userId);
+            for (int p = 0; p < pkgCount; ++p) {
+                final String pkgName = getPackageNameAtIndex(u, p);
+
+                final int tagCount = tagCountForUserAndPackage(userId, pkgName);
+                for (int t = 0; t < tagCount; ++t) {
+                    final String tag = getTagAtIndex(u, p, t);
+                    consumer.accept(userId, pkgName, tag, get(userId, pkgName, tag));
+                }
+            }
+        }
+    }
+
+    interface UptcDataConsumer<D> {
+        void accept(int userId, @NonNull String packageName, @Nullable String tag, @Nullable D obj);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 76d18fa..65d0c4c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -521,7 +521,7 @@
     static final int STARTING_WINDOW_SHOWN = 1;
     static final int STARTING_WINDOW_REMOVED = 2;
     int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
-    boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
+    private boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
 
     // Marking the reason why this activity is being relaunched. Mainly used to track that this
     // activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
@@ -555,7 +555,7 @@
 
     private RemoteAnimationDefinition mRemoteAnimationDefinition;
 
-    private AnimatingActivityRegistry mAnimatingActivityRegistry;
+    AnimatingActivityRegistry mAnimatingActivityRegistry;
 
     private Task mLastParent;
 
@@ -1222,7 +1222,7 @@
     }
 
     ActivityStack getStack() {
-        return task != null ? task.getTaskStack() : null;
+        return task != null ? task.getStack() : null;
     }
 
     @Override
@@ -1269,8 +1269,8 @@
             if (getDisplayContent() != null) {
                 getDisplayContent().mClosingApps.remove(this);
             }
-        } else if (mLastParent != null && mLastParent.getTaskStack() != null) {
-            task.getTaskStack().mExitingActivities.remove(this);
+        } else if (mLastParent != null && mLastParent.getStack() != null) {
+            task.getStack().mExitingActivities.remove(this);
         }
         final ActivityStack stack = getStack();
 
@@ -1974,15 +1974,6 @@
                     + " is already the parent of r=" + this);
         }
 
-        // TODO: Ensure that we do not directly reparent activities across stacks, as that may leave
-        //       the stacks in strange states. For now, we should use Task.reparent() to ensure that
-        //       the stack is left in an OK state.
-        if (prevTask != null && newTask != null && prevTask.getStack() != newTask.getStack()) {
-            throw new IllegalArgumentException(reason + ": task=" + newTask
-                    + " is in a different stack (" + newTask.getStackId() + ") than the parent of"
-                    + " r=" + this + " (" + prevTask.getStackId() + ")");
-        }
-
         ProtoLog.i(WM_DEBUG_ADD_REMOVE, "reparent: moving activity=%s"
                 + " to task=%d at %d", this, task.mTaskId, position);
         reparent(newTask, position);
@@ -2633,14 +2624,13 @@
         // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
         final ActivityRecord next = getDisplay().topRunningActivity(
                 true /* considerKeyguardState */);
-        final boolean isVisible = mVisibleRequested || nowVisible;
         // isNextNotYetVisible is to check if the next activity is invisible, or it has been
         // requested to be invisible but its windows haven't reported as invisible.  If so, it
         // implied that the current finishing activity should be added into stopping list rather
         // than destroy immediately.
         final boolean isNextNotYetVisible = next != null
                 && (!next.nowVisible || !next.mVisibleRequested);
-        if (isVisible && isNextNotYetVisible) {
+        if ((mVisibleRequested || isState(PAUSED)) && isNextNotYetVisible) {
             // Add this activity to the list of stopping activities. It will be processed and
             // destroyed when the next activity reports idle.
             addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
@@ -3098,7 +3088,7 @@
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "Removing app %s delayed=%b animation=%s animating=%b", this, delayed,
-                getAnimation(), isAnimating(TRANSITION));
+                getAnimation(), isAnimating(TRANSITION | PARENTS));
 
         ProtoLog.v(WM_DEBUG_ADD_REMOVE, "removeAppToken: %s"
                 + " delayed=%b Callers=%s", this, delayed, Debug.getCallers(4));
@@ -3110,7 +3100,7 @@
         // If this window was animating, then we need to ensure that the app transition notifies
         // that animations have completed in DisplayContent.handleAnimatingStoppedAndTransition(),
         // so add to that list now
-        if (isAnimating(TRANSITION)) {
+        if (isAnimating(TRANSITION | PARENTS)) {
             getDisplayContent().mNoAnimationNotifyOnTransitionFinished.add(token);
         }
 
@@ -3446,7 +3436,7 @@
      *         color mode set to avoid jank in the middle of the transition.
      */
     boolean canShowWindows() {
-        return allDrawn && !(isAnimating() && hasNonDefaultColorWindow());
+        return allDrawn && !(isAnimating(PARENTS) && hasNonDefaultColorWindow());
     }
 
     /**
@@ -4772,7 +4762,7 @@
         }
 
         // Schedule an idle timeout in case the app doesn't do it for us.
-        mStackSupervisor.scheduleIdleTimeoutLocked(this);
+        mStackSupervisor.scheduleIdleTimeout(this);
 
         mStackSupervisor.reportResumedActivityLocked(this);
 
@@ -4871,8 +4861,7 @@
     void stopIfPossible() {
         if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + this);
         final ActivityStack stack = getActivityStack();
-        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0) {
+        if (isNoHistory()) {
             if (!finishing) {
                 if (!stack.shouldSleepActivities()) {
                     if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + this);
@@ -4993,9 +4982,9 @@
                         + "immediate=" + !idleDelayed);
             }
             if (!idleDelayed) {
-                mStackSupervisor.scheduleIdleLocked();
+                mStackSupervisor.scheduleIdle();
             } else {
-                mStackSupervisor.scheduleIdleTimeoutLocked(this);
+                mStackSupervisor.scheduleIdleTimeout(this);
             }
         } else {
             stack.checkReadyForSleep();
@@ -5354,13 +5343,13 @@
         if (!allDrawn && w.mightAffectAllDrawn()) {
             if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
                 Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
-                        + ", isAnimationSet=" + isAnimating(TRANSITION));
+                        + ", isAnimationSet=" + isAnimating(TRANSITION | PARENTS));
                 if (!w.isDrawnLw()) {
                     Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
                             + " pv=" + w.isVisibleByPolicy()
                             + " mDrawState=" + winAnimator.drawStateToString()
                             + " ph=" + w.isParentWindowHidden() + " th=" + mVisibleRequested
-                            + " a=" + isAnimating(TRANSITION));
+                            + " a=" + isAnimating(TRANSITION | PARENTS));
                 }
             }
 
@@ -5523,7 +5512,7 @@
         if (stack == null) {
             return INVALID_DISPLAY;
         }
-        return stack.mDisplayId;
+        return stack.getDisplayId();
     }
 
     final boolean isDestroyable() {
@@ -5961,7 +5950,7 @@
 
     @Override
     void prepareSurfaces() {
-        final boolean show = isVisible() || isAnimating();
+        final boolean show = isVisible() || isAnimating(PARENTS);
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
@@ -5989,7 +5978,7 @@
     }
 
     void attachThumbnailAnimation() {
-        if (!isAnimating()) {
+        if (!isAnimating(PARENTS)) {
             return;
         }
         final GraphicBuffer thumbnailHeader =
@@ -6009,7 +5998,7 @@
      * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
      */
     void attachCrossProfileAppsThumbnailAnimation() {
-        if (!isAnimating()) {
+        if (!isAnimating(PARENTS)) {
             return;
         }
         clearThumbnail();
@@ -6104,20 +6093,26 @@
         getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
         scheduleAnimation();
 
-        if (mAtmService.mRootWindowContainer.allResumedActivitiesIdle()
-                || mAtmService.mStackSupervisor.isStoppingNoHistoryActivity()) {
-            // If all activities are already idle or there is an activity that must be
-            // stopped immediately after visible, then we now need to make sure we perform
-            // the full stop of this activity. This is because we won't do that while they are still
-            // waiting for the animation to finish.
-            if (mAtmService.mStackSupervisor.mStoppingActivities.contains(this)) {
-                mAtmService.mStackSupervisor.scheduleIdleLocked();
+        if (!mStackSupervisor.mStoppingActivities.isEmpty()
+                || !mStackSupervisor.mFinishingActivities.isEmpty()) {
+            if (mRootWindowContainer.allResumedActivitiesIdle()) {
+                // If all activities are already idle then we now need to make sure we perform
+                // the full stop of this activity. This is because we won't do that while they
+                // are still waiting for the animation to finish.
+                mStackSupervisor.scheduleIdle();
+            } else if (mRootWindowContainer.allResumedActivitiesVisible()) {
+                // If all resumed activities are already visible (and should be drawn, see
+                // updateReportedVisibility ~ nowVisible) but not idle, we still schedule to
+                // process the stopping and finishing activities because the transition is done.
+                // This also avoids if the next activity never reports idle (e.g. animating view),
+                // the previous will need to wait until idle timeout to be stopped or destroyed.
+                mStackSupervisor.scheduleProcessStoppingAndFinishingActivities();
+            } else {
+                // Instead of doing the full stop routine here, let's just hide any activities
+                // we now can, and let them stop when the normal idle happens.
+                mStackSupervisor.processStoppingActivities(null /* launchedActivity */,
+                        true /* onlyUpdateVisibility */, true /* unused */, null /* unused */);
             }
-        } else {
-            // Instead of doing the full stop routine here, let's just hide any activities
-            // we now can, and let them stop when the normal idle happens.
-            mAtmService.mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
-                    false /* remove */, true /* processPausingActivities */);
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
@@ -7451,6 +7446,20 @@
         return this == rootActivity;
     }
 
+    void setTaskOverlay(boolean taskOverlay) {
+        mTaskOverlay = taskOverlay;
+        setAlwaysOnTop(mTaskOverlay);
+    }
+
+    boolean isTaskOverlay() {
+        return mTaskOverlay;
+    }
+
+    @Override
+    boolean showToCurrentUser() {
+        return mShowForAllUsers || mWmService.isCurrentProfile(mUserId);
+    }
+
     @Override
     public String toString() {
         if (stringName != null) {
@@ -7507,7 +7516,7 @@
         super.dumpDebug(proto, WINDOW_TOKEN, logLevel);
         proto.write(LAST_SURFACE_SHOWING, mLastSurfaceShowing);
         proto.write(IS_WAITING_FOR_TRANSITION_START, isWaitingForTransitionStart());
-        proto.write(IS_ANIMATING, isAnimating());
+        proto.write(IS_ANIMATING, isAnimating(PARENTS));
         if (mThumbnail != null){
             mThumbnail.dumpDebug(proto, THUMBNAIL);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 16245f0..c959439 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -70,6 +70,7 @@
 import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
@@ -310,7 +311,7 @@
     int mCurrentUser;
 
     /** The attached Display's unique identifier, or -1 if detached */
-    int mDisplayId;
+    private int mDisplayId;
     // Id of the previous display the stack was on.
     int mPrevDisplayId = INVALID_DISPLAY;
 
@@ -783,6 +784,10 @@
                         null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
                         null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
                         PRESERVE_WINDOWS, true /* deferResume */);
+            } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
+                // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
+                resize(new Rect(newBounds), null /* tempTaskBounds */,
+                        null /* tempTaskInsetBounds */, PRESERVE_WINDOWS, true /* deferResume */);
             }
         }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
@@ -808,8 +813,8 @@
      * @return {@code true} if the windowing mode is transient, {@code false} otherwise.
      */
     private static boolean isTransientWindowingMode(int windowingMode) {
-        // TODO(b/114842032): add PIP if/when it uses mode transitions instead of task reparenting
-        return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+        return windowingMode == WINDOWING_MODE_PINNED
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
     }
 
@@ -1013,6 +1018,10 @@
         return getDisplayContent();
     }
 
+    int getDisplayId() {
+        return mDisplayId;
+    }
+
     /**
      * Defers updating the bounds of the stack. If the stack was resized/repositioned while
      * deferring, the bounds will update in {@link #continueUpdateBounds()}.
@@ -1082,7 +1091,7 @@
     }
 
     private ActivityRecord topRunningNonOverlayTaskActivity() {
-        return getActivity((r) -> (r.canBeTopRunning() && !r.mTaskOverlay));
+        return getActivity((r) -> (r.canBeTopRunning() && !r.isTaskOverlay()));
     }
 
     ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
@@ -1253,7 +1262,7 @@
 
         super.switchUser(userId);
         forAllTasks((t) -> {
-            if (t.mWmService.isCurrentProfileLocked(t.mUserId) || t.showForAllUsers()) {
+            if (t.mWmService.isCurrentProfile(t.mUserId) || t.showForAllUsers()) {
                 mChildren.remove(t);
                 mChildren.add(t);
             }
@@ -1271,7 +1280,7 @@
         // Make sure that there is no activity waiting for this to launch.
         if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) {
             mStackSupervisor.removeIdleTimeoutForActivity(r);
-            mStackSupervisor.scheduleIdleTimeoutLocked(r);
+            mStackSupervisor.scheduleIdleTimeout(r);
         }
     }
 
@@ -1324,7 +1333,7 @@
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
                         + mStackSupervisor.mStoppingActivities.size() + " activities");
 
-                mStackSupervisor.scheduleIdleLocked();
+                mStackSupervisor.scheduleIdle();
                 shouldSleep = false;
             }
 
@@ -1409,8 +1418,7 @@
         else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
         mPausingActivity = prev;
         mLastPausedActivity = prev;
-        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
-                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
+        mLastNoHistoryActivity = prev.isNoHistory() ? prev : null;
         prev.setState(PAUSING, "startPausingLocked");
         prev.getTask().touchActiveTime();
         clearLaunchTime(prev);
@@ -1830,6 +1838,15 @@
                 && (topTask == null || topTask.supportsSplitScreenWindowingMode());
     }
 
+    /**
+     * Returns {@code true} if this is the top-most split-screen-primary or
+     * split-screen-secondary stack, {@code false} otherwise.
+     */
+    boolean isTopSplitScreenStack() {
+        return inSplitScreenWindowingMode()
+                && this == getDisplay().getTopStackInWindowingMode(getWindowingMode());
+    }
+
     /** @return True if the resizing of the primary-split-screen stack affects this stack size. */
     boolean affectedBySplitScreenResize() {
         if (!supportsSplitScreenWindowingMode()) {
@@ -2665,7 +2682,7 @@
         final Task task = taskTop.getTask();
 
         // If ActivityOptions are moved out and need to be aborted or moved to taskTop.
-        final ActivityOptions topOptions = sResetTargetTaskHelper.process(this, task, forceReset);
+        final ActivityOptions topOptions = sResetTargetTaskHelper.process(task, forceReset);
 
         if (mChildren.contains(task)) {
             final ActivityRecord newTop = task.getTopNonFinishingActivity();
@@ -2999,6 +3016,11 @@
 
     final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
             AppTimeTracker timeTracker, String reason) {
+        moveTaskToFrontLocked(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
+    }
+
+    final void moveTaskToFrontLocked(Task tr, boolean noAnimation, ActivityOptions options,
+            AppTimeTracker timeTracker, boolean deferResume, String reason) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
 
         final ActivityStack topStack = getDisplay().getTopStack();
@@ -3070,7 +3092,9 @@
                 topActivity.supportsEnterPipOnTaskSwitch = true;
             }
 
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
+            if (!deferResume) {
+                mRootWindowContainer.resumeFocusedStacksTopActivities();
+            }
             EventLogTags.writeWmTaskToFront(tr.mUserId, tr.mTaskId);
             mAtmService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.getTaskInfo());
         } finally {
@@ -3965,11 +3989,19 @@
      * @param showForAllUsers Whether to show the task regardless of the current user.
      */
     private void addChild(Task task, int position, boolean showForAllUsers, boolean moveParents) {
-        // Add child task.
-        addChild(task, null);
+        try {
+            // Force show for all user so task can be position correctly based on which user is
+            // active. We clear the force show below.
+            task.setForceShowForAllUsers(showForAllUsers);
+            // Add child task.
+            addChild(task, null);
 
-        // Move child to a proper position, as some restriction for position might apply.
-        positionChildAt(position, task, moveParents /* includingParents */, showForAllUsers);
+            // Move child to a proper position, as some restriction for position might apply.
+            positionChildAt(position, task, moveParents /* includingParents */);
+
+        } finally {
+            task.setForceShowForAllUsers(false);
+        }
     }
 
     @Override
@@ -4015,18 +4047,7 @@
     @Override
     void positionChildAt(int position, WindowContainer child, boolean includingParents) {
         final Task task = (Task) child;
-        positionChildAt(position, task, includingParents, task.showForAllUsers());
-    }
-
-    /**
-     * Overridden version of {@link ActivityStack#positionChildAt(int, WindowContainer, boolean)}.
-     * Used in {@link ActivityStack#addChild(Task, int, boolean showForAllUsers, boolean)}, as it
-     * can receive showForAllUsers param from {@link ActivityRecord} instead of
-     * {@link Task#showForAllUsers()}.
-     */
-    private int positionChildAt(int position, Task child, boolean includingParents,
-            boolean showForAllUsers) {
-        final int targetPosition = findPositionForTask(child, position, showForAllUsers);
+        final int targetPosition = findPositionForTask(task, position);
         super.positionChildAt(targetPosition, child, includingParents);
 
         // Log positioning.
@@ -4035,8 +4056,7 @@
         }
 
         final int toTop = targetPosition == mChildren.size() - 1 ? 1 : 0;
-        EventLogTags.writeWmTaskMoved(child.mTaskId, toTop, targetPosition);
-        return targetPosition;
+        EventLogTags.writeWmTaskMoved(task.mTaskId, toTop, targetPosition);
     }
 
     @Override
@@ -4106,9 +4126,8 @@
 
     // TODO: We should really have users as a window container in the hierarchy so that we don't
     // have to do complicated things like we are doing in this method.
-    int findPositionForTask(Task task, int targetPosition, boolean showForAllUsers) {
-        final boolean canShowTask =
-                showForAllUsers || mWmService.isCurrentProfileLocked(task.mUserId);
+    int findPositionForTask(Task task, int targetPosition) {
+        final boolean canShowTask = task.showToCurrentUser();
 
         final int stackSize = mChildren.size();
         int minPosition = 0;
@@ -4140,9 +4159,7 @@
     private int computeMinPosition(int minPosition, int size) {
         while (minPosition < size) {
             final Task tmpTask = (Task) mChildren.get(minPosition);
-            final boolean canShowTmpTask =
-                    tmpTask.showForAllUsers()
-                            || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
+            final boolean canShowTmpTask = tmpTask.showToCurrentUser();
             if (canShowTmpTask) {
                 break;
             }
@@ -4160,9 +4177,7 @@
     private int computeMaxPosition(int maxPosition) {
         while (maxPosition > 0) {
             final Task tmpTask = (Task) mChildren.get(maxPosition);
-            final boolean canShowTmpTask =
-                    tmpTask.showForAllUsers()
-                            || mWmService.isCurrentProfileLocked(tmpTask.mUserId);
+            final boolean canShowTmpTask = tmpTask.showToCurrentUser();
             if (!canShowTmpTask) {
                 break;
             }
@@ -4877,6 +4892,18 @@
         }
     }
 
+    @Override
+    protected void onAnimationFinished() {
+        super.onAnimationFinished();
+        // TODO(b/142617871): we may need to add animation type parameter on onAnimationFinished to
+        //  identify if the callback is for launch animation finish and then calling
+        //  activity#onAnimationFinished.
+        final ActivityRecord activity = getTopMostActivity();
+        if (activity != null) {
+            activity.onAnimationFinished();
+        }
+    }
+
     /**
      * Sets the current picture-in-picture aspect ratio.
      */
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a380efc4..e8564fc 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -183,6 +183,7 @@
     private static final int RESUME_TOP_ACTIVITY_MSG = FIRST_SUPERVISOR_STACK_MSG + 2;
     private static final int SLEEP_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 3;
     private static final int LAUNCH_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 4;
+    private static final int PROCESS_STOPPING_AND_FINISHING_MSG = FIRST_SUPERVISOR_STACK_MSG + 5;
     private static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
     private static final int RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
     private static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
@@ -571,14 +572,14 @@
         }
     }
 
-    void setNextTaskIdForUserLocked(int taskId, int userId) {
+    void setNextTaskIdForUser(int taskId, int userId) {
         final int currentTaskId = mCurTaskIdForUser.get(userId, -1);
         if (taskId > currentTaskId) {
             mCurTaskIdForUser.put(userId, taskId);
         }
     }
 
-    static int nextTaskIdForUser(int taskId, int userId) {
+    private static int nextTaskIdForUser(int taskId, int userId) {
         int nextTaskId = taskId + 1;
         if (nextTaskId == (userId + 1) * MAX_TASK_IDS_PER_USER) {
             // Wrap around as there will be smaller task ids that are available now.
@@ -587,7 +588,11 @@
         return nextTaskId;
     }
 
-    int getNextTaskIdForUserLocked(int userId) {
+    int getNextTaskIdForUser() {
+        return getNextTaskIdForUser(mRootWindowContainer.mCurrentUser);
+    }
+
+    int getNextTaskIdForUser(int userId) {
         final int currentTaskId = mCurTaskIdForUser.get(userId, userId * MAX_TASK_IDS_PER_USER);
         // for a userId u, a taskId can only be in the range
         // [u*MAX_TASK_IDS_PER_USER, (u+1)*MAX_TASK_IDS_PER_USER-1], so if MAX_TASK_IDS_PER_USER
@@ -1211,8 +1216,8 @@
         }
 
         // TODO moltmann b/136595429: Set featureId from caller
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage, /* featureId */ null)
-                != AppOpsManager.MODE_ALLOWED) {
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
+                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
             if (!ignoreTargetSecurity) {
                 return ACTIVITY_RESTRICTION_APPOP;
             }
@@ -1254,9 +1259,9 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        // TODO moltmann b/136595429: Set componentId from caller
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid, callingPackage, /* featureId */ null)
-                != AppOpsManager.MODE_ALLOWED) {
+        // TODO moltmann b/136595429: Set featureId from caller
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
+                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
             return ACTIVITY_RESTRICTION_APPOP;
         }
 
@@ -1297,22 +1302,14 @@
         return booting;
     }
 
-    // Checked.
-    @GuardedBy("mService")
-    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
+    void activityIdleInternal(ActivityRecord r, boolean fromTimeout,
             boolean processPausingActivities, Configuration config) {
-        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);
+        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + r);
 
-        ArrayList<ActivityRecord> finishes = null;
-        ArrayList<UserState> startingUsers = null;
-        int NS = 0;
-        int NF = 0;
         boolean booting = false;
-        boolean activityRemoved = false;
 
-        ActivityRecord r = ActivityRecord.forTokenLocked(token);
         if (r != null) {
-            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
+            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternal: Callers="
                     + Debug.getCallers(4));
             mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
             r.finishLaunchTickingLocked();
@@ -1365,47 +1362,14 @@
         }
 
         // Atomically retrieve all of the other things to do.
-        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
-                true /* remove */, processPausingActivities);
-        NS = stops != null ? stops.size() : 0;
-        if ((NF = mFinishingActivities.size()) > 0) {
-            finishes = new ArrayList<>(mFinishingActivities);
-            mFinishingActivities.clear();
-        }
+        processStoppingAndFinishingActivities(r, processPausingActivities, "idle");
 
-        if (mStartingUsers.size() > 0) {
-            startingUsers = new ArrayList<>(mStartingUsers);
+        if (!mStartingUsers.isEmpty()) {
+            final ArrayList<UserState> startingUsers = new ArrayList<>(mStartingUsers);
             mStartingUsers.clear();
-        }
 
-        // Stop any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (int i = 0; i < NS; i++) {
-            r = stops.get(i);
-            final ActivityStack stack = r.getActivityStack();
-            if (stack != null) {
-                if (r.finishing) {
-                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
-                    r.destroyIfPossible("activityIdleInternalLocked");
-                } else {
-                    r.stopIfPossible();
-                }
-            }
-        }
-
-        // Finish any activities that are scheduled to do so but have been
-        // waiting for the next one to start.
-        for (int i = 0; i < NF; i++) {
-            r = finishes.get(i);
-            final ActivityStack stack = r.getActivityStack();
-            if (stack != null) {
-                activityRemoved |= r.destroyImmediately(true /* removeFromApp */, "finish-idle");
-            }
-        }
-
-        if (!booting) {
-            // Complete user switch
-            if (startingUsers != null) {
+            if (!booting) {
+                // Complete user switch.
                 for (int i = 0; i < startingUsers.size(); i++) {
                     mService.mAmInternal.finishUserSwitch(startingUsers.get(i));
                 }
@@ -1413,14 +1377,6 @@
         }
 
         mService.mH.post(() -> mService.mAmInternal.trimApplications());
-        //dump();
-        //mWindowManager.dump();
-
-        if (activityRemoved) {
-            mRootWindowContainer.resumeFocusedStacksTopActivities();
-        }
-
-        return r;
     }
 
     /** This doesn't just find a task, it also moves the task to front. */
@@ -1761,7 +1717,7 @@
             stack.mForceHidden = true;
             stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
             stack.mForceHidden = false;
-            activityIdleInternalLocked(null, false /* fromTimeout */,
+            activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                     true /* processPausingActivities */, null /* configuration */);
 
             // Move all the tasks to the bottom of the fullscreen stack
@@ -1955,7 +1911,7 @@
 
         // Ensure that we're not moving a task to a dynamic stack if device doesn't support
         // multi-display.
-        if (stack.mDisplayId != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
+        if (stack.getDisplayId() != DEFAULT_DISPLAY && !mService.mSupportsMultiDisplay) {
             throw new IllegalArgumentException("Device doesn't support multi-display, can not"
                     + " reparent task=" + task + " to stackId=" + stackId);
         }
@@ -2113,35 +2069,45 @@
     }
 
     /**
-     * Returns whether a stopping activity is present that should be stopped after visible, rather
-     * than idle.
-     * @return {@code true} if such activity is present. {@code false} otherwise.
+     * Processes the activities to be stopped or destroyed. This should be called when the resumed
+     * activities are idle or drawn.
      */
-    boolean isStoppingNoHistoryActivity() {
-        // Activities that are marked as nohistory should be stopped immediately after the resumed
-        // activity has become visible.
-        for (ActivityRecord record : mStoppingActivities) {
-            if (record.isNoHistory()) {
-                return true;
-            }
+    private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity,
+            boolean processPausingActivities, String reason) {
+        // Stop any activities that are scheduled to do so but have been waiting for the transition
+        // animation to finish.
+        processStoppingActivities(launchedActivity, false /* onlyUpdateVisibility */,
+                processPausingActivities, reason);
+
+        final int numFinishingActivities = mFinishingActivities.size();
+        if (numFinishingActivities == 0) {
+            return;
         }
 
-        return false;
+        // Finish any activities that are scheduled to do so but have been waiting for the next one
+        // to start.
+        final ArrayList<ActivityRecord> finishingActivities = new ArrayList<>(mFinishingActivities);
+        mFinishingActivities.clear();
+        for (int i = 0; i < numFinishingActivities; i++) {
+            final ActivityRecord r = finishingActivities.get(i);
+            if (r.isInHistory()) {
+                r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
+            }
+        }
     }
 
-    // TODO: Change method name to reflect what it actually does.
-    final ArrayList<ActivityRecord> processStoppingActivitiesLocked(ActivityRecord idleActivity,
-            boolean remove, boolean processPausingActivities) {
-        ArrayList<ActivityRecord> stops = null;
+    /** Stop or destroy the stopping activities if they are not interactive. */
+    void processStoppingActivities(ActivityRecord launchedActivity, boolean onlyUpdateVisibility,
+            boolean processPausingActivities, String reason) {
+        final int numStoppingActivities = mStoppingActivities.size();
+        if (numStoppingActivities == 0) {
+            return;
+        }
+        ArrayList<ActivityRecord> readyToStopActivities = null;
 
         final boolean nowVisible = mRootWindowContainer.allResumedActivitiesVisible();
-        for (int activityNdx = mStoppingActivities.size() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord s = mStoppingActivities.get(activityNdx);
-
-            final boolean animating = s.isAnimating(TRANSITION);
-
-            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
-                    + " animating=" + animating + " finishing=" + s.finishing);
+        for (int activityNdx = numStoppingActivities - 1; activityNdx >= 0; --activityNdx) {
+            final ActivityRecord s = mStoppingActivities.get(activityNdx);
             if (nowVisible && s.finishing) {
 
                 // If this activity is finishing, it is sitting on top of
@@ -2152,32 +2118,49 @@
                 if (DEBUG_STATES) Slog.v(TAG, "Before stopping, can hide: " + s);
                 s.setVisibility(false);
             }
-            if (remove) {
-                final ActivityStack stack = s.getActivityStack();
-                final boolean shouldSleepOrShutDown = stack != null
-                        ? stack.shouldSleepOrShutDownActivities()
-                        : mService.isSleepingOrShuttingDownLocked();
-                if (!animating || shouldSleepOrShutDown) {
-                    if (!processPausingActivities && s.isState(PAUSING)) {
-                        // Defer processing pausing activities in this iteration and reschedule
-                        // a delayed idle to reprocess it again
-                        removeIdleTimeoutForActivity(idleActivity);
-                        scheduleIdleTimeoutLocked(idleActivity);
-                        continue;
-                    }
+            if (onlyUpdateVisibility) {
+                continue;
+            }
 
-                    if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
-                    if (stops == null) {
-                        stops = new ArrayList<>();
-                    }
-                    stops.add(s);
+            final boolean animating = s.isAnimating(TRANSITION);
+            if (DEBUG_STATES) Slog.v(TAG, "Stopping " + s + ": nowVisible=" + nowVisible
+                    + " animating=" + animating + " finishing=" + s.finishing);
 
-                    mStoppingActivities.remove(activityNdx);
+            final ActivityStack stack = s.getActivityStack();
+            final boolean shouldSleepOrShutDown = stack != null
+                    ? stack.shouldSleepOrShutDownActivities()
+                    : mService.isSleepingOrShuttingDownLocked();
+            if (!animating || shouldSleepOrShutDown) {
+                if (!processPausingActivities && s.isState(PAUSING)) {
+                    // Defer processing pausing activities in this iteration and reschedule
+                    // a delayed idle to reprocess it again
+                    removeIdleTimeoutForActivity(launchedActivity);
+                    scheduleIdleTimeout(launchedActivity);
+                    continue;
                 }
+
+                if (DEBUG_STATES) Slog.v(TAG, "Ready to stop: " + s);
+                if (readyToStopActivities == null) {
+                    readyToStopActivities = new ArrayList<>();
+                }
+                readyToStopActivities.add(s);
+
+                mStoppingActivities.remove(activityNdx);
             }
         }
 
-        return stops;
+        final int numReadyStops = readyToStopActivities == null ? 0 : readyToStopActivities.size();
+        for (int i = 0; i < numReadyStops; i++) {
+            final ActivityRecord r = readyToStopActivities.get(i);
+            if (r.isInHistory()) {
+                if (r.finishing) {
+                    // TODO(b/137329632): Wait for idle of the right activity, not just any.
+                    r.destroyIfPossible(reason);
+                } else {
+                    r.stopIfPossible();
+                }
+            }
+        }
     }
 
     void removeHistoryRecords(WindowProcessController app) {
@@ -2315,14 +2298,13 @@
         return printed;
     }
 
-    void scheduleIdleTimeoutLocked(ActivityRecord next) {
-        if (DEBUG_IDLE) Slog.d(TAG_IDLE,
-                "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
+    void scheduleIdleTimeout(ActivityRecord next) {
+        if (DEBUG_IDLE) Slog.d(TAG_IDLE, "scheduleIdleTimeout: Callers=" + Debug.getCallers(4));
         Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
         mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
     }
 
-    final void scheduleIdleLocked() {
+    final void scheduleIdle() {
         mHandler.sendEmptyMessage(IDLE_NOW_MSG);
     }
 
@@ -2405,6 +2387,12 @@
         }
     }
 
+    void scheduleProcessStoppingAndFinishingActivities() {
+        if (!mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)) {
+            mHandler.sendEmptyMessage(PROCESS_STOPPING_AND_FINISHING_MSG);
+        }
+    }
+
     void removeSleepTimeouts() {
         mHandler.removeMessages(SLEEP_TIMEOUT_MSG);
     }
@@ -2443,7 +2431,7 @@
 
         // Handle incorrect launch/move to secondary display if needed.
         if (isSecondaryDisplayPreferred) {
-            final int actualDisplayId = task.getStack().mDisplayId;
+            final int actualDisplayId = task.getDisplayId();
             if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
                 throw new IllegalStateException("Task resolved to incompatible display");
             }
@@ -2616,83 +2604,19 @@
 
     private final class ActivityStackSupervisorHandler extends Handler {
 
-        public ActivityStackSupervisorHandler(Looper looper) {
+        ActivityStackSupervisorHandler(Looper looper) {
             super(looper);
         }
 
-        void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
-            synchronized (mService.mGlobalLock) {
-                activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
-                        processPausingActivities, null);
-            }
-        }
-
         @Override
         public void handleMessage(Message msg) {
+            synchronized (mService.mGlobalLock) {
+                if (handleMessageInner(msg)) {
+                    return;
+                }
+            }
+            // The cases that some invocations cannot be locked by WM.
             switch (msg.what) {
-                case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
-                            final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
-                            r.updateMultiWindowMode();
-                        }
-                    }
-                } break;
-                case REPORT_PIP_MODE_CHANGED_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
-                            final ActivityRecord r = mPipModeChangedActivities.remove(i);
-                            r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
-                                    false /* forceUpdate */);
-                        }
-                    }
-                } break;
-                case IDLE_TIMEOUT_MSG: {
-                    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
-                            "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
-                    // We don't at this point know if the activity is fullscreen,
-                    // so we need to be conservative and assume it isn't.
-                    activityIdleInternal((ActivityRecord) msg.obj,
-                            true /* processPausingActivities */);
-                } break;
-                case IDLE_NOW_MSG: {
-                    if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
-                    activityIdleInternal((ActivityRecord) msg.obj,
-                            false /* processPausingActivities */);
-                } break;
-                case RESUME_TOP_ACTIVITY_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        mRootWindowContainer.resumeFocusedStacksTopActivities();
-                    }
-                } break;
-                case SLEEP_TIMEOUT_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        if (mService.isSleepingOrShuttingDownLocked()) {
-                            Slog.w(TAG, "Sleep timeout!  Sleeping now.");
-                            checkReadyForSleepLocked(false /* allowDelay */);
-                        }
-                    }
-                } break;
-                case LAUNCH_TIMEOUT_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        if (mLaunchingActivityWakeLock.isHeld()) {
-                            Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
-                            if (VALIDATE_WAKE_LOCK_CALLER
-                                    && Binder.getCallingUid() != Process.myUid()) {
-                                throw new IllegalStateException("Calling must be system uid");
-                            }
-                            mLaunchingActivityWakeLock.release();
-                        }
-                    }
-                } break;
-                case LAUNCH_TASK_BEHIND_COMPLETE: {
-                    synchronized (mService.mGlobalLock) {
-                        ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
-                        if (r != null) {
-                            handleLaunchTaskBehindCompleteLocked(r);
-                        }
-                    }
-                } break;
                 case RESTART_ACTIVITY_PROCESS_TIMEOUT_MSG: {
                     final ActivityRecord r = (ActivityRecord) msg.obj;
                     String processName = null;
@@ -2709,26 +2633,93 @@
                                 "restartActivityProcessTimeout");
                     }
                 } break;
-                case REPORT_HOME_CHANGED_MSG: {
-                    synchronized (mService.mGlobalLock) {
-                        mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
+            }
+        }
 
-                        // Start home activities on displays with no activities.
-                        mRootWindowContainer.startHomeOnEmptyDisplays("homeChanged");
+        private void activityIdleFromMessage(ActivityRecord idleActivity, boolean fromTimeout) {
+            activityIdleInternal(idleActivity, fromTimeout,
+                    fromTimeout /* processPausingActivities */, null /* config */);
+        }
+
+        /**
+         * Handles the message with lock held.
+         *
+         * @return {@code true} if the message is handled.
+         */
+        private boolean handleMessageInner(Message msg) {
+            switch (msg.what) {
+                case REPORT_MULTI_WINDOW_MODE_CHANGED_MSG: {
+                    for (int i = mMultiWindowModeChangedActivities.size() - 1; i >= 0; i--) {
+                        final ActivityRecord r = mMultiWindowModeChangedActivities.remove(i);
+                        r.updateMultiWindowMode();
                     }
                 } break;
-                case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: {
-                    ActivityRecord r = (ActivityRecord) msg.obj;
-                    Slog.w(TAG, "Activity top resumed state loss timeout for " + r);
-                    synchronized (mService.mGlobalLock) {
-                        if (r.hasProcess()) {
-                            mService.logAppTooSlow(r.app, r.topResumedStateLossTime,
-                                    "top state loss for " + r);
+                case REPORT_PIP_MODE_CHANGED_MSG: {
+                    for (int i = mPipModeChangedActivities.size() - 1; i >= 0; i--) {
+                        final ActivityRecord r = mPipModeChangedActivities.remove(i);
+                        r.updatePictureInPictureMode(mPipModeChangedTargetStackBounds,
+                                false /* forceUpdate */);
+                    }
+                } break;
+                case IDLE_TIMEOUT_MSG: {
+                    if (DEBUG_IDLE) Slog.d(TAG_IDLE,
+                            "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
+                    // We don't at this point know if the activity is fullscreen, so we need to be
+                    // conservative and assume it isn't.
+                    activityIdleFromMessage((ActivityRecord) msg.obj, true /* fromTimeout */);
+                } break;
+                case IDLE_NOW_MSG: {
+                    if (DEBUG_IDLE) Slog.d(TAG_IDLE, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
+                    activityIdleFromMessage((ActivityRecord) msg.obj, false /* fromTimeout */);
+                } break;
+                case RESUME_TOP_ACTIVITY_MSG: {
+                    mRootWindowContainer.resumeFocusedStacksTopActivities();
+                } break;
+                case SLEEP_TIMEOUT_MSG: {
+                    if (mService.isSleepingOrShuttingDownLocked()) {
+                        Slog.w(TAG, "Sleep timeout!  Sleeping now.");
+                        checkReadyForSleepLocked(false /* allowDelay */);
+                    }
+                } break;
+                case LAUNCH_TIMEOUT_MSG: {
+                    if (mLaunchingActivityWakeLock.isHeld()) {
+                        Slog.w(TAG, "Launch timeout has expired, giving up wake lock!");
+                        if (VALIDATE_WAKE_LOCK_CALLER
+                                && Binder.getCallingUid() != Process.myUid()) {
+                            throw new IllegalStateException("Calling must be system uid");
                         }
+                        mLaunchingActivityWakeLock.release();
+                    }
+                } break;
+                case PROCESS_STOPPING_AND_FINISHING_MSG: {
+                    processStoppingAndFinishingActivities(null /* launchedActivity */,
+                            false /* processPausingActivities */, "transit");
+                } break;
+                case LAUNCH_TASK_BEHIND_COMPLETE: {
+                    final ActivityRecord r = ActivityRecord.forTokenLocked((IBinder) msg.obj);
+                    if (r != null) {
+                        handleLaunchTaskBehindCompleteLocked(r);
+                    }
+                } break;
+                case REPORT_HOME_CHANGED_MSG: {
+                    mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
+
+                    // Start home activities on displays with no activities.
+                    mRootWindowContainer.startHomeOnEmptyDisplays("homeChanged");
+                } break;
+                case TOP_RESUMED_STATE_LOSS_TIMEOUT_MSG: {
+                    final ActivityRecord r = (ActivityRecord) msg.obj;
+                    Slog.w(TAG, "Activity top resumed state loss timeout for " + r);
+                    if (r.hasProcess()) {
+                        mService.logAppTooSlow(r.app, r.topResumedStateLossTime,
+                                "top state loss for " + r);
                     }
                     handleTopResumedStateReleased(true /* timeout */);
                 } break;
+                default:
+                    return false;
             }
+            return true;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 955e581..75d87ed 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -135,7 +135,7 @@
         mHandler = new StartHandler(mService.mH.getLooper());
         mFactory = factory;
         mFactory.setController(this);
-        mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
+        mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service.mGlobalLock,
                 service.mH);
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index a2b9d95..61ba15c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -29,8 +29,6 @@
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WaitResult.LAUNCH_STATE_COLD;
 import static android.app.WaitResult.LAUNCH_STATE_HOT;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -65,10 +63,8 @@
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
@@ -1073,7 +1069,8 @@
                             mRootWindowContainer.getTopDisplayFocusedStack();
                     Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true,
                             true, false) + "} from uid " + callingUid + " on display "
-                            + (focusedStack == null ? DEFAULT_DISPLAY : focusedStack.mDisplayId));
+                            + (focusedStack == null ? DEFAULT_DISPLAY
+                                    : focusedStack.getDisplayId()));
                 }
             }
         }
@@ -1487,11 +1484,6 @@
         mIntent.setFlags(mLaunchFlags);
 
         final Task reusedTask = getReusableTask();
-        mSupervisor.getLaunchParamsController().calculate(reusedTask != null ? reusedTask : mInTask,
-                r.info.windowLayout, r, sourceRecord, options, PHASE_BOUNDS, mLaunchParams);
-        mPreferredDisplayId =
-                mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
-                        : DEFAULT_DISPLAY;
 
         // If requested, freeze the task list
         if (mOptions != null && mOptions.freezeRecentTasksReordering()
@@ -1505,6 +1497,8 @@
         final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
         final boolean newTask = targetTask == null;
 
+        computeLaunchParams(r, sourceRecord, targetTask);
+
         // Check if starting activity on given task or on a new task is allowed.
         int startResult = isAllowedToStart(r, newTask, targetTask);
         if (startResult != START_SUCCESS) {
@@ -1532,7 +1526,7 @@
         }
 
         if (mTargetStack == null) {
-            mTargetStack = computeStackFocus(mStartActivity, true, mLaunchFlags, mOptions);
+            mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, targetTask, mOptions);
         }
         if (newTask) {
             final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
@@ -1576,7 +1570,7 @@
             final ActivityRecord topTaskActivity =
                     mStartActivity.getTask().topRunningActivityLocked();
             if (!mTargetStack.isFocusable()
-                    || (topTaskActivity != null && topTaskActivity.mTaskOverlay
+                    || (topTaskActivity != null && topTaskActivity.isTaskOverlay()
                     && mStartActivity != topTaskActivity)) {
                 // If the activity is not focusable, we can't resume it, but still would like to
                 // make sure it becomes visible as it starts (this will also trigger entry
@@ -1622,15 +1616,49 @@
         } else if (mInTask != null) {
             return mInTask;
         } else {
-            final ActivityRecord top = computeStackFocus(mStartActivity, false /* newTask */,
-                    mLaunchFlags, mOptions).getTopNonFinishingActivity();
+            final ActivityStack stack = getLaunchStack(mStartActivity, mLaunchFlags,
+                    null /* task */, mOptions);
+            final ActivityRecord top = stack.getTopNonFinishingActivity();
             if (top != null) {
                 return top.getTask();
+            } else {
+                // Remove the stack if no activity in the stack.
+                stack.removeIfPossible();
             }
         }
         return null;
     }
 
+    private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
+            Task targetTask) {
+        final ActivityStack sourceStack = mSourceStack != null ? mSourceStack
+                : mRootWindowContainer.getTopDisplayFocusedStack();
+        if (sourceStack != null && sourceStack.inSplitScreenWindowingMode()
+                && (mOptions == null
+                        || mOptions.getLaunchWindowingMode() == WINDOWING_MODE_UNDEFINED)) {
+            int windowingMode =
+                    targetTask != null ? targetTask.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+            if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
+                if (sourceStack.inSplitScreenPrimaryWindowingMode()) {
+                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+                } else if (sourceStack.inSplitScreenSecondaryWindowingMode()) {
+                    windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+                }
+            }
+
+            if (mOptions == null) {
+                mOptions = ActivityOptions.makeBasic();
+            }
+            mOptions.setLaunchWindowingMode(windowingMode);
+        }
+
+        mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
+                sourceRecord, mOptions, PHASE_BOUNDS, mLaunchParams);
+        mPreferredDisplayId =
+                mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId
+                        : DEFAULT_DISPLAY;
+    }
+
     private int isAllowedToStart(ActivityRecord r, boolean newTask, Task targetTask) {
         if (mStartActivity.packageName == null) {
             if (mStartActivity.resultTo != null) {
@@ -1869,8 +1897,8 @@
                 if (targetTask.getStack() == null) {
                     // Target stack got cleared when we all activities were removed above.
                     // Go ahead and reset it.
-                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
-                            mLaunchFlags, mOptions);
+                    mTargetStack =
+                            getLaunchStack(mStartActivity, mLaunchFlags, null /* task */, mOptions);
                     mTargetStack.addChild(targetTask, !mLaunchTaskBehind /* toTop */,
                             (mStartActivity.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
                 }
@@ -2044,7 +2072,7 @@
 
         if (mOptions != null) {
             if (mOptions.getLaunchTaskId() != -1 && mOptions.getTaskOverlay()) {
-                r.mTaskOverlay = true;
+                r.setTaskOverlay(true);
                 if (!mOptions.canTaskOverlayResume()) {
                     final Task task = mRootWindowContainer.anyTaskForId(
                             mOptions.getLaunchTaskId());
@@ -2293,7 +2321,7 @@
         // the same behavior as if a new instance was being started, which means not bringing it
         // to the front if the caller is not itself in the front.
         final boolean differentTopTask;
-        if (mPreferredDisplayId == mTargetStack.mDisplayId) {
+        if (mPreferredDisplayId == mTargetStack.getDisplayId()) {
             final ActivityStack focusStack = mTargetStack.getDisplay().getFocusedStack();
             final ActivityRecord curTop = (focusStack == null)
                     ? null : focusStack.topRunningNonDelayedActivityLocked(mNotTop);
@@ -2319,57 +2347,25 @@
                 final ActivityStack launchStack =
                         getLaunchStack(mStartActivity, mLaunchFlags, intentTask, mOptions);
                 if (launchStack == null || launchStack == mTargetStack) {
+                    // Do not set mMovedToFront to true below for split-screen-top stack, or
+                    // START_TASK_TO_FRONT will be returned and trigger unexpected animations when a
+                    // new intent has delivered.
+                    final boolean isSplitScreenTopStack = mTargetStack.isTopSplitScreenStack();
+
                     // We only want to move to the front, if we aren't going to launch on a
                     // different stack. If we launch on a different stack, we will put the
                     // task on top there.
+                    // Defer resuming the top activity while moving task to top, since the
+                    // current task-top activity may not be the activity that should be resumed.
                     mTargetStack.moveTaskToFrontLocked(intentTask, mNoAnimation, mOptions,
-                            mStartActivity.appTimeTracker, "bringingFoundTaskToFront");
-                    mMovedToFront = true;
-                } else if (launchStack.inSplitScreenWindowingMode()) {
-                    if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
-                        // If we want to launch adjacent and mTargetStack is not the computed
-                        // launch stack - move task to top of computed stack.
-                        intentTask.reparent(launchStack, ON_TOP,
-                                REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
-                                "launchToSide");
-                    } else {
-                        // TODO: This should be reevaluated in MW v2.
-                        // We choose to move task to front instead of launching it adjacent
-                        // when specific stack was requested explicitly and it appeared to be
-                        // adjacent stack, but FLAG_ACTIVITY_LAUNCH_ADJACENT was not set.
-                        mTargetStack.moveTaskToFrontLocked(intentTask,
-                                mNoAnimation, mOptions, mStartActivity.appTimeTracker,
-                                "bringToFrontInsteadOfAdjacentLaunch");
-                    }
-                    mMovedToFront = launchStack != launchStack.getDisplay()
-                            .getTopStackInWindowingMode(launchStack.getWindowingMode());
-                } else if (launchStack.mDisplayId != mTargetStack.mDisplayId) {
-                    // Target and computed stacks are on different displays and we've
-                    // found a matching task - move the existing instance to that display and
-                    // move it to front.
-                    intentActivity.getTask().reparent(launchStack, ON_TOP,
-                            REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
-                            "reparentToDisplay");
-                    mMovedToFront = true;
-                } else if (launchStack.isActivityTypeHome()
-                        && !mTargetStack.isActivityTypeHome()) {
-                    // It is possible for the home activity to be in another stack initially.
-                    // For example, the activity may have been initially started with an intent
-                    // which placed it in the fullscreen stack. To ensure the proper handling of
-                    // the activity based on home stack assumptions, we must move it over.
-                    intentActivity.getTask().reparent(launchStack, ON_TOP,
-                            REPARENT_MOVE_STACK_TO_FRONT, ANIMATE, DEFER_RESUME,
-                            "reparentingHome");
+                            mStartActivity.appTimeTracker, DEFER_RESUME,
+                            "bringingFoundTaskToFront");
+                    mMovedToFront = !isSplitScreenTopStack;
+                } else {
+                    intentTask.reparent(launchStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, ANIMATE,
+                            DEFER_RESUME, "reparentToTargetStack");
                     mMovedToFront = true;
                 }
-
-                if (launchStack != null && launchStack.getTopMostTask() == null) {
-                    // The task does not need to be reparented to the launch stack. Remove the
-                    // launch stack if there is no activity in it.
-                    Slog.w(TAG, "Removing an empty stack: " + launchStack);
-                    launchStack.removeIfPossible();
-                }
-
                 mOptions = null;
             }
         }
@@ -2392,7 +2388,7 @@
     private void setNewTask(Task taskToAffiliate) {
         final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
         final Task task = mTargetStack.createTask(
-                mSupervisor.getNextTaskIdForUserLocked(mStartActivity.mUserId),
+                mSupervisor.getNextTaskIdForUser(mStartActivity.mUserId),
                 mNewTaskInfo != null ? mNewTaskInfo : mStartActivity.info,
                 mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
                 mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
@@ -2469,90 +2465,6 @@
         return launchFlags;
     }
 
-    private ActivityStack computeStackFocus(ActivityRecord r, boolean newTask, int launchFlags,
-            ActivityOptions aOptions) {
-        final Task task = r.getTask();
-        ActivityStack stack = getLaunchStack(r, launchFlags, task, aOptions);
-        if (stack != null) {
-            return stack;
-        }
-
-        final ActivityStack currentStack = task != null ? task.getStack() : null;
-        final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
-        if (currentStack != null) {
-            if (focusedStack != currentStack) {
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Setting " + "focused stack to r=" + r
-                                + " task=" + task);
-            } else {
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Focused stack already=" + focusedStack);
-            }
-            return currentStack;
-        }
-
-        if (canLaunchIntoFocusedStack(r, newTask)) {
-            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                    "computeStackFocus: Have a focused stack=" + focusedStack);
-            return focusedStack;
-        }
-
-        if (mPreferredDisplayId != DEFAULT_DISPLAY) {
-            // Try to put the activity in a stack on a secondary display.
-            stack = mRootWindowContainer.getValidLaunchStackOnDisplay(
-                    mPreferredDisplayId, r, aOptions, mLaunchParams);
-            if (stack == null) {
-                // If source display is not suitable - look for topmost valid stack in the system.
-                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
-                        "computeStackFocus: Can't launch on mPreferredDisplayId="
-                                + mPreferredDisplayId + ", looking on all displays.");
-                stack = mRootWindowContainer.getNextValidLaunchStack(r, mPreferredDisplayId);
-            }
-        }
-        if (stack == null) {
-            stack = mRootWindowContainer.getLaunchStack(r, aOptions, task, ON_TOP);
-        }
-        if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS, "computeStackFocus: New stack r="
-                + r + " stackId=" + stack.mStackId);
-        return stack;
-    }
-
-    /** Check if provided activity record can launch in currently focused stack. */
-    // TODO: This method can probably be consolidated into getLaunchStack() below.
-    private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
-        final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
-        final boolean canUseFocusedStack;
-        if (focusedStack.isActivityTypeAssistant()) {
-            canUseFocusedStack = r.isActivityTypeAssistant();
-        } else {
-            switch (focusedStack.getWindowingMode()) {
-                case WINDOWING_MODE_FULLSCREEN:
-                    // The fullscreen stack can contain any task regardless of if the task is
-                    // resizeable or not. So, we let the task go in the fullscreen task if it is the
-                    // focus stack.
-                    canUseFocusedStack = true;
-                    break;
-                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
-                    // Any activity which supports split screen can go in the docked stack.
-                    canUseFocusedStack = r.supportsSplitScreenWindowingMode();
-                    break;
-                case WINDOWING_MODE_FREEFORM:
-                    // Any activity which supports freeform can go in the freeform stack.
-                    canUseFocusedStack = r.supportsFreeform();
-                    break;
-                default:
-                    // Dynamic stacks behave similarly to the fullscreen stack and can contain any
-                    // resizeable task.
-                    canUseFocusedStack = !focusedStack.isOnHomeDisplay()
-                            && r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
-            }
-        }
-        return canUseFocusedStack && !newTask
-                // Using the focus stack isn't important enough to override the preferred display.
-                && (mPreferredDisplayId == focusedStack.mDisplayId);
-    }
-
     private ActivityStack getLaunchStack(ActivityRecord r, int launchFlags, Task task,
             ActivityOptions aOptions) {
         // We are reusing a task, keep the stack!
@@ -2560,53 +2472,10 @@
             return mReuseTask.getStack();
         }
 
-        if (((launchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) == 0)
-                 || mPreferredDisplayId != DEFAULT_DISPLAY) {
-            final boolean onTop =
-                    (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
-            final ActivityStack stack =
-                    mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
-                            mRequest.realCallingPid, mRequest.realCallingUid);
-            return stack;
-        }
-        // Otherwise handle adjacent launch.
-
-        final ActivityStack focusedStack = mRootWindowContainer.getTopDisplayFocusedStack();
-        // The parent activity doesn't want to launch the activity on top of itself, but
-        // instead tries to put it onto other side in side-by-side mode.
-        final ActivityStack parentStack = task != null ? task.getStack(): focusedStack;
-
-        if (parentStack != focusedStack) {
-            // If task's parent stack is not focused - use it during adjacent launch.
-            return parentStack;
-        } else {
-            if (focusedStack != null && task == focusedStack.getTopMostTask()) {
-                // If task is already on top of focused stack - use it. We don't want to move the
-                // existing focused task to adjacent stack, just deliver new intent in this case.
-                return focusedStack;
-            }
-
-            if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
-                // If parent was in docked stack, the natural place to launch another activity
-                // will be fullscreen, so it can appear alongside the docked window.
-                final int activityType =
-                        mRootWindowContainer.resolveActivityType(r, mOptions, task);
-                return parentStack.getDisplay().getOrCreateStack(
-                        WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, activityType, ON_TOP);
-            } else {
-                // If the parent is not in the docked stack, we check if there is docked window
-                // and if yes, we will launch into that stack. If not, we just put the new
-                // activity into parent's stack, because we can't find a better place.
-                final ActivityStack dockedStack =
-                        mRootWindowContainer.getDefaultDisplay().getSplitScreenPrimaryStack();
-                if (dockedStack != null && !dockedStack.shouldBeVisible(r)) {
-                    // There is a docked stack, but it isn't visible, so we can't launch into that.
-                    return mRootWindowContainer.getLaunchStack(r, aOptions, task, ON_TOP);
-                } else {
-                    return dockedStack;
-                }
-            }
-        }
+        final boolean onTop =
+                (aOptions == null || !aOptions.getAvoidMoveToFront()) && !mLaunchTaskBehind;
+        return mRootWindowContainer.getLaunchStack(r, aOptions, task, onTop, mLaunchParams,
+                mRequest.realCallingPid, mRequest.realCallingUid);
     }
 
     private boolean isLaunchModeOneOf(int mode1, int mode2) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ac1dbc4..76c0e4e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -296,6 +296,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -901,7 +902,7 @@
 
     boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
         final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                callingUid, callingPackage, /* featureId */ null);
+                callingUid, callingPackage, /* featureId */ null, false, "");
         if (mode == AppOpsManager.MODE_DEFAULT) {
             return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
                     == PERMISSION_GRANTED;
@@ -1684,20 +1685,16 @@
     public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
         final long origId = Binder.clearCallingIdentity();
         try {
-            WindowProcessController proc = null;
             synchronized (mGlobalLock) {
                 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
-                ActivityStack stack = ActivityRecord.getStackLocked(token);
-                if (stack == null) {
+                final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+                if (r == null) {
                     return;
                 }
-                final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
-                        false /* fromTimeout */, false /* processPausingActivities */, config);
-                if (r != null) {
-                    proc = r.app;
-                }
-                if (stopProfiling && proc != null) {
-                    proc.clearProfilerIfNeeded();
+                mStackSupervisor.activityIdleInternal(r, false /* fromTimeout */,
+                        false /* processPausingActivities */, config);
+                if (stopProfiling && r.hasProcess()) {
+                    r.app.clearProfilerIfNeeded();
                 }
             }
         } finally {
@@ -2042,8 +2039,8 @@
     public int getDisplayId(IBinder activityToken) throws RemoteException {
         synchronized (mGlobalLock) {
             final ActivityStack stack = ActivityRecord.getStackLocked(activityToken);
-            if (stack != null && stack.mDisplayId != INVALID_DISPLAY) {
-                return stack.mDisplayId;
+            if (stack != null && stack.getDisplayId() != INVALID_DISPLAY) {
+                return stack.getDisplayId();
             }
             return DEFAULT_DISPLAY;
         }
@@ -3201,7 +3198,7 @@
 
                 final ActivityStack stack = r.getActivityStack();
                 final Task task = stack.createTask(
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), ainfo, intent,
+                        mStackSupervisor.getNextTaskIdForUser(r.mUserId), ainfo, intent,
                         null /* voiceSession */, null /* voiceInteractor */, !ON_TOP);
                 if (!mRecentTasks.addToBottom(task)) {
                     // The app has too many tasks already and we can't add any more
@@ -4311,7 +4308,7 @@
 
         if (params.hasSetAspectRatio()
                 && !mWindowManager.isValidPictureInPictureAspectRatio(
-                        r.getActivityStack().mDisplayId, params.getAspectRatio())) {
+                        r.getDisplayId(), params.getAspectRatio())) {
             final float minAspectRatio = mContext.getResources().getFloat(
                     com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
             final float maxAspectRatio = mContext.getResources().getFloat(
@@ -6164,7 +6161,7 @@
     final class LocalService extends ActivityTaskManagerInternal {
         @Override
         public SleepToken acquireSleepToken(String tag, int displayId) {
-            Preconditions.checkNotNull(tag);
+            Objects.requireNonNull(tag);
             return ActivityTaskManagerService.this.acquireSleepToken(tag, displayId);
         }
 
@@ -6221,7 +6218,7 @@
         @Override
         public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
                 Bundle bOptions) {
-            Preconditions.checkNotNull(intents, "intents");
+            Objects.requireNonNull(intents, "intents");
             final String[] resolvedTypes = new String[intents.length];
 
             // UID of the package on user userId.
@@ -6904,7 +6901,7 @@
                 if (mRootWindowContainer.finishDisabledPackageActivities(
                         packageName, disabledClasses, true, false, userId) && booted) {
                     mRootWindowContainer.resumeFocusedStacksTopActivities();
-                    mStackSupervisor.scheduleIdleLocked();
+                    mStackSupervisor.scheduleIdle();
                 }
 
                 // Clean-up disabled tasks
@@ -6931,7 +6928,7 @@
             synchronized (mGlobalLock) {
                 mRootWindowContainer.resumeFocusedStacksTopActivities();
                 if (scheduleIdle) {
-                    mStackSupervisor.scheduleIdleLocked();
+                    mStackSupervisor.scheduleIdle();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 3a33a3d..09111d0 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -436,9 +436,11 @@
         mNextAppTransition = TRANSIT_UNSET;
         mNextAppTransitionFlags = 0;
         setAppTransitionState(APP_STATE_RUNNING);
-        final AnimationAdapter topOpeningAnim = topOpeningApp != null
-                ? topOpeningApp.getAnimation()
-                : null;
+        final AnimationAdapter topOpeningAnim =
+                (topOpeningApp != null && topOpeningApp.getAnimatingContainer() != null)
+                        ? topOpeningApp.getAnimatingContainer().getAnimation()
+                        : null;
+
         int redoLayout = notifyAppTransitionStartingLocked(transit,
                 topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
                 topOpeningAnim != null
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 51742b9..7f94445 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -230,7 +230,6 @@
 import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.util.function.pooled.PooledPredicate;
-import com.android.server.AnimationThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 import com.android.server.wm.utils.DisplayRotationUtil;
@@ -600,11 +599,6 @@
 
     private final SparseArray<ShellRoot> mShellRoots = new SparseArray<>();
 
-    /**
-     * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
-     */
-    private static int sNextFreeStackId = 0;
-
     private RootWindowContainer mRootWindowContainer;
 
     /**
@@ -982,7 +976,7 @@
 
         AnimationHandler animationHandler = new AnimationHandler();
         mBoundsAnimationController = new BoundsAnimationController(mWmService.mContext,
-                mAppTransition, AnimationThread.getHandler(), animationHandler);
+                mAppTransition, mWmService.mAnimationHandler, animationHandler);
 
         final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
                 "PointerEventDispatcher" + mDisplayId, mDisplayId);
@@ -1038,31 +1032,13 @@
         // Sets the display content for the children.
         onDisplayChanged(this);
 
-        // Add itself as a child to the root container.
-        mWmService.mRoot.addChild(this, POSITION_BOTTOM);
-
-        // TODO(b/62541591): evaluate whether this is the best spot to declare the
-        // {@link DisplayContent} ready for use.
-        mDisplayReady = true;
-
-        mWmService.mAnimator.addDisplayLocked(mDisplayId);
-        mInputMonitor = new InputMonitor(mWmService, mDisplayId);
+        mInputMonitor = new InputMonitor(mWmService, this);
         mInsetsStateController = new InsetsStateController(this);
         mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
 
-        if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
+        if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
 
         mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
-
-        if (mWmService.mDisplayManagerInternal != null) {
-            mWmService.mDisplayManagerInternal
-                    .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
-            configureDisplayPolicy();
-        }
-
-        reconfigureDisplayLocked();
-        onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
-        mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
     }
 
     boolean isReady() {
@@ -3366,7 +3342,7 @@
                 // to look at all windows below the current target that are in this app, finding the
                 // highest visible one in layering.
                 WindowState highestTarget = null;
-                if (activity.isAnimating(TRANSITION)) {
+                if (activity.isAnimating(PARENTS | TRANSITION)) {
                     highestTarget = activity.getHighestAnimLayerWindow(curTarget);
                 }
 
@@ -4808,10 +4784,8 @@
             } else {
                 final int count = mChildren.size();
                 for (int i = 0; i < count; i++) {
-                    Slog.d(TAG, "child " + mChildren.get(i));
                     final WindowContainer child = mChildren.get(i);
                     if (skipTraverseChild(child)) {
-                        Slog.d(TAG, "child skipped");
                         continue;
                     }
 
@@ -5000,6 +4974,24 @@
         // we create the root surfaces explicitly rather than chaining
         // up as the default implementation in onParentChanged does. So we
         // explicitly do NOT call super here.
+
+        if (!isReady()) {
+            // TODO(b/62541591): evaluate whether this is the best spot to declare the
+            // {@link DisplayContent} ready for use.
+            mDisplayReady = true;
+
+            mWmService.mAnimator.addDisplayLocked(mDisplayId);
+
+            if (mWmService.mDisplayManagerInternal != null) {
+                mWmService.mDisplayManagerInternal
+                        .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
+                configureDisplayPolicy();
+            }
+
+            reconfigureDisplayLocked();
+            onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+            mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
+        }
     }
 
     @Override
@@ -5694,7 +5686,7 @@
 
     @VisibleForTesting
     int getNextStackId() {
-        return sNextFreeStackId++;
+        return mAtmService.mStackSupervisor.getNextTaskIdForUser();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 694a73d..f9ad03f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2192,10 +2192,13 @@
         final boolean attachedInParent = attached != null && !layoutInScreen;
         final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0
                 || (requestedSysUiFl & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0
-                || !win.getClientInsetsState().getSource(ITYPE_STATUS_BAR).isVisible();
+                || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
+                        && !win.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
         final boolean requestedHideNavigation =
                 (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
-                        || !win.getClientInsetsState().getSource(ITYPE_NAVIGATION_BAR).isVisible();
+                || (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL
+                        && !win.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
+                                .isVisible());
 
         // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
         // cropped / shifted to the displayFrame in WindowState.
diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java
index d5f403f..999aab9 100644
--- a/services/core/java/com/android/server/wm/DragDropController.java
+++ b/services/core/java/com/android/server/wm/DragDropController.java
@@ -37,6 +37,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.wm.WindowManagerInternal.IDragDropCallback;
 
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -74,7 +75,7 @@
     }
 
     void registerCallback(IDragDropCallback callback) {
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(callback);
         mCallback.set(callback);
     }
 
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 255ef6e..a26dfdb 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -276,7 +276,6 @@
                     display.getDisplayId());
             mDragWindowHandle.name = "drag";
             mDragWindowHandle.token = mServerChannel.getToken();
-            mDragWindowHandle.layer = getDragLayerLocked();
             mDragWindowHandle.layoutParamsFlags = 0;
             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
             mDragWindowHandle.dispatchingTimeoutNanos =
@@ -345,12 +344,6 @@
         }
     }
 
-    int getDragLayerLocked() {
-        return mService.mPolicy.getWindowLayerFromTypeLw(WindowManager.LayoutParams.TYPE_DRAG)
-                * WindowManagerService.TYPE_LAYER_MULTIPLIER
-                + WindowManagerService.TYPE_LAYER_OFFSET;
-    }
-
     /* call out to each visible window/session informing it about the drag
      */
     void broadcastDragStartedLocked(final float touchX, final float touchY) {
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index e74f61d..55f5e28 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -156,7 +156,8 @@
             // determined individually unlike other stacks where the visibility or fullscreen
             // status of an activity in a previous task affects other.
             mBehindFullscreenActivity = !mContainerShouldBeVisible;
-        } else if (mContiner.isActivityTypeHome()) {
+        } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+                && r.isRootOfTask()) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
                     + " stackShouldBeVisible=" + mContainerShouldBeVisible
                     + " behindFullscreenActivity=" + mBehindFullscreenActivity);
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index ebe9f08..c6183de 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -76,7 +76,6 @@
         mWindowHandle.name = name;
         mWindowHandle.token = mServerChannel.getToken();
         mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
-        mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
         mWindowHandle.layoutParamsFlags = 0;
         mWindowHandle.dispatchingTimeoutNanos =
                 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
@@ -150,12 +149,6 @@
         t.setLayer(mInputSurface, layer);
     }
 
-    private int getLayerLw(int windowType) {
-        return mService.mPolicy.getWindowLayerFromTypeLw(windowType)
-                * WindowManagerService.TYPE_LAYER_MULTIPLIER
-                + WindowManagerService.TYPE_LAYER_OFFSET;
-    }
-
     void disposeChannelsLw() {
         mService.mInputManager.unregisterInputChannel(mServerChannel);
         mClientChannel.dispose();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 1f9f883..5b892f8 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -10,23 +10,40 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
 
+import android.os.Build;
 import android.os.Debug;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.IWindow;
 import android.view.InputApplicationHandle;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 
+import com.android.server.am.ActivityManagerService;
 import com.android.server.input.InputManagerService;
 import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
 
+import java.io.File;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 final class InputManagerCallback implements InputManagerService.WindowManagerCallbacks {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "InputManagerCallback" : TAG_WM;
+
+    /** Prevent spamming the traces because pre-dump cannot aware duplicated ANR. */
+    private static final long PRE_DUMP_MIN_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
+    /** The timeout to detect if a monitor is held for a while. */
+    private static final long PRE_DUMP_MONITOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(1);
+    /** The last time pre-dump was executed. */
+    private volatile long mLastPreDumpTimeMs;
+
     private final WindowManagerService mService;
 
     // Set to true when the first input device configuration change notification
@@ -77,6 +94,77 @@
     }
 
     /**
+     * Pre-dump stack trace if the locks of activity manager or window manager (they may be locked
+     * in the path of reporting ANR) cannot be acquired in time. That provides the stack traces
+     * before the real blocking symptom has gone.
+     * <p>
+     * Do not hold the {@link WindowManagerGlobalLock} while calling this method.
+     */
+    private void preDumpIfLockTooSlow() {
+        if (!Build.IS_DEBUGGABLE)  {
+            return;
+        }
+        final long now = SystemClock.uptimeMillis();
+        if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) {
+            return;
+        }
+
+        final boolean[] shouldDumpSf = { true };
+        final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
+        monitors.put(TAG_WM, mService::monitor);
+        monitors.put("ActivityManager", mService.mAmInternal::monitor);
+        final CountDownLatch latch = new CountDownLatch(monitors.size());
+        // The pre-dump will execute if one of the monitors doesn't complete within the timeout.
+        for (int i = 0; i < monitors.size(); i++) {
+            final String name = monitors.keyAt(i);
+            final Runnable monitor = monitors.valueAt(i);
+            // Always create new thread to avoid noise of existing threads. Suppose here won't
+            // create too many threads because it means that watchdog will be triggered first.
+            new Thread() {
+                @Override
+                public void run() {
+                    monitor.run();
+                    latch.countDown();
+                    final long elapsed = SystemClock.uptimeMillis() - now;
+                    if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
+                        Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
+                    } else if (TAG_WM.equals(name)) {
+                        // Window manager is the main client of SurfaceFlinger. If window manager
+                        // is responsive, the stack traces of SurfaceFlinger may not be important.
+                        shouldDumpSf[0] = false;
+                    }
+                };
+            }.start();
+        }
+        try {
+            if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                return;
+            }
+        } catch (InterruptedException ignored) { }
+        mLastPreDumpTimeMs = now;
+        Slog.i(TAG_WM, "Pre-dump for unresponsive");
+
+        final ArrayList<Integer> firstPids = new ArrayList<>(1);
+        firstPids.add(ActivityManagerService.MY_PID);
+        ArrayList<Integer> nativePids = null;
+        final int[] pids = shouldDumpSf[0]
+                ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
+                : null;
+        if (pids != null) {
+            nativePids = new ArrayList<>(1);
+            for (int pid : pids) {
+                nativePids.add(pid);
+            }
+        }
+
+        final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+                null /* processCpuTracker */, null /* lastPids */, nativePids);
+        if (tracesFile != null) {
+            tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
+        }
+    }
+
+    /**
      * Notifies the window manager about an application that is not responding.
      * Returns a new timeout to continue waiting in nanoseconds, or 0 to abort dispatch.
      *
@@ -89,6 +177,9 @@
         WindowState windowState = null;
         boolean aboveSystem = false;
         int windowPid = INVALID_PID;
+
+        preDumpIfLockTooSlow();
+
         //TODO(b/141764879) Limit scope of wm lock when input calls notifyANR
         synchronized (mService.mGlobalLock) {
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f9ff2e3..c4b67d7 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -47,7 +47,6 @@
 import android.view.InputWindowHandle;
 import android.view.SurfaceControl;
 
-import com.android.server.AnimationThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 
@@ -152,12 +151,12 @@
 
     private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
 
-    public InputMonitor(WindowManagerService service, int displayId) {
+    InputMonitor(WindowManagerService service, DisplayContent displayContent) {
         mService = service;
-        mDisplayContent = mService.mRoot.getDisplayContent(displayId);
-        mDisplayId = displayId;
+        mDisplayContent = displayContent;
+        mDisplayId = displayContent.getDisplayId();
         mInputTransaction = mService.mTransactionFactory.get();
-        mHandler = AnimationThread.getHandler();
+        mHandler = mService.mAnimationHandler;
         mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
     }
 
@@ -264,7 +263,6 @@
         inputWindowHandle.hasFocus = hasFocus;
         inputWindowHandle.hasWallpaper = hasWallpaper;
         inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
-        inputWindowHandle.layer = child.mLayer;
         inputWindowHandle.ownerPid = child.mSession.mPid;
         inputWindowHandle.ownerUid = child.mSession.mUid;
         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
@@ -499,8 +497,7 @@
                 }
             }
 
-            if (mAddInputConsumerHandle
-                    && inputWindowHandle.layer <= navInputConsumer.mWindowHandle.layer) {
+            if (mAddInputConsumerHandle) {
                 navInputConsumer.show(mInputTransaction, w);
                 mAddInputConsumerHandle = false;
             }
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index af84836..a008963 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -66,10 +66,11 @@
         }
         mStatusBar.setVisible(focusedWin == null
                 || focusedWin != getStatusControlTarget(focusedWin)
-                || focusedWin.getClientInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
+                || focusedWin.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
         mNavBar.setVisible(focusedWin == null
                 || focusedWin != getNavControlTarget(focusedWin)
-                || focusedWin.getClientInsetsState().getSource(ITYPE_NAVIGATION_BAR).isVisible());
+                || focusedWin.getRequestedInsetsState().getSource(ITYPE_NAVIGATION_BAR)
+                        .isVisible());
     }
 
     boolean isHidden(@InternalInsetsType int type) {
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 0beae7e..f4e608e 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -222,9 +222,7 @@
 
     private boolean saveTaskToLaunchParam(Task task, PersistableLaunchParams params) {
         final ActivityStack stack = task.getStack();
-        final int displayId = stack.mDisplayId;
-        final DisplayContent display =
-                mSupervisor.mRootWindowContainer.getDisplayContent(displayId);
+        final DisplayContent display = stack.getDisplayContent();
         final DisplayInfo info = new DisplayInfo();
         display.mDisplay.getDisplayInfo(info);
 
diff --git a/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
index dcb9a6a..3b8631a 100644
--- a/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
+++ b/services/core/java/com/android/server/wm/PendingRemoteAnimationRegistry.java
@@ -22,12 +22,10 @@
 import android.util.ArrayMap;
 import android.view.RemoteAnimationAdapter;
 
-import com.android.server.am.ActivityManagerService;
-
 /**
  * Registry to keep track of remote animations to be run for activity starts from a certain package.
  *
- * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart
+ * @see ActivityTaskManagerService#registerRemoteAnimationForNextActivityStart
  */
 class PendingRemoteAnimationRegistry {
 
@@ -35,10 +33,10 @@
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
     private final Handler mHandler;
-    private final ActivityTaskManagerService mService;
+    private final WindowManagerGlobalLock mLock;
 
-    PendingRemoteAnimationRegistry(ActivityTaskManagerService service, Handler handler) {
-        mService = service;
+    PendingRemoteAnimationRegistry(WindowManagerGlobalLock lock, Handler handler) {
+        mLock = lock;
         mHandler = handler;
     }
 
@@ -76,7 +74,7 @@
             this.packageName = packageName;
             this.adapter = adapter;
             mHandler.postDelayed(() -> {
-                synchronized (mService.mGlobalLock) {
+                synchronized (mLock) {
                     final Entry entry = mEntries.get(packageName);
                     if (entry == this) {
                         mEntries.remove(packageName);
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index a18d541..5df80fc 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1382,7 +1382,7 @@
 
         // Ignore tasks from different displays
         // TODO (b/115289124): No Recents on non-default displays.
-        if (stack.mDisplayId != DEFAULT_DISPLAY) {
+        if (stack.getDisplayId() != DEFAULT_DISPLAY) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 0b9be1a..6f7eeab 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -378,9 +378,10 @@
 
         int getMode() {
             final DisplayContent dc = mWindowContainer.getDisplayContent();
-            if (dc.mOpeningApps.contains(mWindowContainer)) {
+            final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
+            if (dc.mOpeningApps.contains(topActivity)) {
                 return RemoteAnimationTarget.MODE_OPENING;
-            } else if (dc.mChangingApps.contains(mWindowContainer)) {
+            } else if (dc.mChangingApps.contains(topActivity)) {
                 return RemoteAnimationTarget.MODE_CHANGING;
             } else {
                 return RemoteAnimationTarget.MODE_CLOSING;
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index f78c82b..e310fc1 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -37,8 +37,8 @@
 /** Helper class for processing the reset of a task. */
 class ResetTargetTaskHelper {
     private Task mTask;
-    private ActivityStack mParent;
     private Task mTargetTask;
+    private ActivityStack mTargetStack;
     private ActivityRecord mRoot;
     private boolean mForceReset;
     private boolean mCanMoveOptions;
@@ -47,7 +47,7 @@
     private ActivityOptions mTopOptions;
     private ArrayList<ActivityRecord> mResultActivities = new ArrayList<>();
     private ArrayList<ActivityRecord> mAllActivities = new ArrayList<>();
-    private ArrayList<Task> mCreatedTasks = new ArrayList<>();
+    private ArrayList<ActivityRecord> mPendingReparentActivities = new ArrayList<>();
 
     private void reset(Task task) {
         mTask = task;
@@ -56,23 +56,22 @@
         mTopOptions = null;
         mResultActivities.clear();
         mAllActivities.clear();
-        mCreatedTasks.clear();
     }
 
-    ActivityOptions process(ActivityStack parent, Task targetTask, boolean forceReset) {
-        mParent = parent;
+    ActivityOptions process(Task targetTask, boolean forceReset) {
         mForceReset = forceReset;
         mTargetTask = targetTask;
         mTargetTaskFound = false;
+        mTargetStack = targetTask.getStack();
         mActivityReparentPosition = -1;
 
         final PooledConsumer c = PooledLambda.obtainConsumer(
                 ResetTargetTaskHelper::processTask, this, PooledLambda.__(Task.class));
-        parent.forAllTasks(c);
+        targetTask.mWmService.mRoot.forAllTasks(c);
         c.recycle();
 
+        processPendingReparentActivities();
         reset(null);
-        mParent = null;
         return mTopOptions;
     }
 
@@ -89,8 +88,6 @@
                 PooledLambda.__(ActivityRecord.class), isTargetTask);
         task.forAllActivities(f);
         f.recycle();
-
-        processCreatedTasks();
     }
 
     private boolean processActivity(ActivityRecord r, boolean isTargetTask) {
@@ -122,32 +119,9 @@
                     // it out of here. We will move it as far out of the way as possible, to the
                     // bottom of the activity stack. This also keeps it correctly ordered with
                     // any activities we previously moved.
-                    // TODO: We should probably look for other stacks also, since corresponding
-                    //  task with the same affinity is unlikely to be in the same stack.
-                    final Task targetTask;
-                    final ActivityRecord bottom = mParent.getBottomMostActivity();
 
-                    if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
-                        // If the activity currently at the bottom has the same task affinity as
-                        // the one we are moving, then merge it into the same task.
-                        targetTask = bottom.getTask();
-                        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
-                                + r + " out to bottom task " + targetTask);
-                    } else {
-                        targetTask = mParent.createTask(
-                                mParent.mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId),
-                                r.info, null /* intent */, null /* voiceSession */,
-                                null /* voiceInteractor */, false /* toTop */);
-                        targetTask.affinityIntent = r.intent;
-                        mCreatedTasks.add(targetTask);
-                        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
-                                + r + " out to new task " + targetTask);
-                    }
-
-                    mResultActivities.add(r);
-                    processResultActivities(r, targetTask, 0 /*bottom*/, true, true);
-                    mParent.positionChildAtBottom(targetTask);
-                    mParent.mStackSupervisor.mRecentTasks.add(targetTask);
+                    // Handle this activity after we have done traversing the hierarchy.
+                    mPendingReparentActivities.add(r);
                     return false;
                 }
             }
@@ -203,8 +177,6 @@
                 processResultActivities(
                         r, mTargetTask, mActivityReparentPosition, false, false);
 
-                mParent.positionChildAtTop(mTargetTask);
-
                 // Now we've moved it in to place...but what if this is a singleTop activity and
                 // we have put it on top of another instance of the same activity? Then we drop
                 // the instance below so it remains singleTop.
@@ -255,23 +227,51 @@
         }
     }
 
-    private void processCreatedTasks() {
-        if (mCreatedTasks.isEmpty()) return;
-
-        DisplayContent display = mParent.getDisplay();
-        final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
-        if (singleTaskInstanceDisplay) {
-            display = mParent.mRootWindowContainer.getDefaultDisplay();
+    private void processPendingReparentActivities() {
+        if (mPendingReparentActivities.isEmpty()) {
+            return;
         }
 
-        final int windowingMode = mParent.getWindowingMode();
-        final int activityType = mParent.getActivityType();
+        final ActivityTaskManagerService atmService = mTargetStack.mAtmService;
+        final ArrayList<Task> createdTasks = new ArrayList<>();
+        while (!mPendingReparentActivities.isEmpty()) {
+            final ActivityRecord r = mPendingReparentActivities.remove(0);
+            final ActivityRecord bottom = mTargetStack.getBottomMostActivity();
+            final Task targetTask;
+            if (bottom != null && r.taskAffinity.equals(bottom.getTask().affinity)) {
+                // If the activity currently at the bottom has the same task affinity as
+                // the one we are moving, then merge it into the same task.
+                targetTask = bottom.getTask();
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+                        + r + " out to bottom task " + targetTask);
+            } else {
+                targetTask = mTargetStack.createTask(
+                        atmService.mStackSupervisor.getNextTaskIdForUser(r.mUserId), r.info,
+                        null /* intent */, null /* voiceSession */, null /* voiceInteractor */,
+                        false /* toTop */);
+                targetTask.affinityIntent = r.intent;
+                createdTasks.add(targetTask);
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity "
+                        + r + " out to new task " + targetTask);
+            }
+            r.reparent(targetTask, 0 /* position */, "resetTargetTaskIfNeeded");
+            atmService.mStackSupervisor.mRecentTasks.add(targetTask);
+        }
+
+        DisplayContent display = mTargetStack.getDisplay();
+        final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
+        if (singleTaskInstanceDisplay) {
+            display = atmService.mRootWindowContainer.getDefaultDisplay();
+        }
+
+        final int windowingMode = mTargetStack.getWindowingMode();
+        final int activityType = mTargetStack.getActivityType();
         if (!singleTaskInstanceDisplay && !display.alwaysCreateStack(windowingMode, activityType)) {
             return;
         }
 
-        while (!mCreatedTasks.isEmpty()) {
-            final Task targetTask = mCreatedTasks.remove(mCreatedTasks.size() - 1);
+        while (!createdTasks.isEmpty()) {
+            final Task targetTask = createdTasks.remove(createdTasks.size() - 1);
             final ActivityStack targetStack = display.getOrCreateStack(
                     windowingMode, activityType, false /* onTop */);
             targetTask.reparent(targetStack, false /* toTop */, REPARENT_LEAVE_STACK_IN_PLACE,
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5470327..a7bf660 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1377,6 +1377,7 @@
         for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
             final Display display = displays[displayNdx];
             final DisplayContent displayContent = new DisplayContent(display, this);
+            addChild(displayContent, POSITION_BOTTOM);
             if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
                 mDefaultDisplay = displayContent;
             }
@@ -1445,6 +1446,7 @@
         }
         // The display hasn't been added to ActivityManager yet, create a new record now.
         displayContent = new DisplayContent(display, this);
+        addChild(displayContent, POSITION_BOTTOM);
         return displayContent;
     }
 
@@ -1495,7 +1497,7 @@
         // Fallback to top focused display if the displayId is invalid.
         if (displayId == INVALID_DISPLAY) {
             final ActivityStack stack = getTopDisplayFocusedStack();
-            displayId = stack != null ? stack.mDisplayId : DEFAULT_DISPLAY;
+            displayId = stack != null ? stack.getDisplayId() : DEFAULT_DISPLAY;
         }
 
         Intent homeIntent = null;
@@ -2148,7 +2150,7 @@
                 // ensures that all the necessary work to migrate states in the old and new stacks
                 // is also done.
                 final Task newTask = task.getStack().createTask(
-                        mStackSupervisor.getNextTaskIdForUserLocked(r.mUserId), r.info,
+                        mStackSupervisor.getNextTaskIdForUser(r.mUserId), r.info,
                         r.intent, null, null, true);
                 r.reparent(newTask, MAX_VALUE, "moveActivityToStack");
 
@@ -2405,11 +2407,10 @@
     }
 
     private ActivityManager.StackInfo getStackInfo(ActivityStack stack) {
-        final int displayId = stack.mDisplayId;
-        final DisplayContent display = getDisplayContent(displayId);
+        final DisplayContent display = stack.getDisplayContent();
         ActivityManager.StackInfo info = new ActivityManager.StackInfo();
         stack.getBounds(info.bounds);
-        info.displayId = displayId;
+        info.displayId = display.mDisplayId;
         info.stackId = stack.mStackId;
         info.stackToken = stack.mRemoteToken;
         info.userId = stack.mCurrentUser;
@@ -2571,7 +2572,7 @@
     }
 
     ActivityStack findStackBehind(ActivityStack stack) {
-        final DisplayContent display = getDisplayContent(stack.mDisplayId);
+        final DisplayContent display = getDisplayContent(stack.getDisplayId());
         if (display != null) {
             for (int i = display.getStackCount() - 1; i >= 0; i--) {
                 if (display.getStackAt(i) == stack && i > 0) {
@@ -2806,7 +2807,6 @@
             int realCallingUid) {
         int taskId = INVALID_TASK_ID;
         int displayId = INVALID_DISPLAY;
-        //Rect bounds = null;
 
         // We give preference to the launch preference in activity options.
         if (options != null) {
@@ -2828,7 +2828,7 @@
         }
 
         final int activityType = resolveActivityType(r, options, candidateTask);
-        ActivityStack stack;
+        ActivityStack stack = null;
 
         // Next preference for stack goes to the display Id set the candidate display.
         if (launchParams != null && launchParams.mPreferredDisplayId != INVALID_DISPLAY) {
@@ -2861,7 +2861,6 @@
 
         // Give preference to the stack and display of the input task and activity if they match the
         // mode we want to launch into.
-        stack = null;
         DisplayContent display = null;
         if (candidateTask != null) {
             stack = candidateTask.getStack();
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 5198602..3b349b8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -463,7 +463,7 @@
             final WindowState windowState = mService.windowForClientLocked(this, window,
                     false /* throwOnError */);
             if (windowState != null) {
-                windowState.setClientInsetsState(state);
+                windowState.updateRequestedInsetsState(state);
                 windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(
                         windowState, state);
             }
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index bbd986f..50cea2e 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -27,6 +27,7 @@
 import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.hardware.power.V1_0.PowerHint;
+import android.os.Handler;
 import android.os.PowerManagerInternal;
 import android.util.ArrayMap;
 import android.view.Choreographer;
@@ -57,6 +58,8 @@
     @VisibleForTesting
     Choreographer mChoreographer;
 
+    private final Handler mAnimationThreadHandler = AnimationThread.getHandler();
+    private final Handler mSurfaceAnimationHandler = SurfaceAnimationThread.getHandler();
     private final Runnable mApplyTransactionRunnable = this::applyTransaction;
     private final AnimationHandler mAnimationHandler;
     private final Transaction mFrameTransaction;
@@ -85,7 +88,7 @@
     SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider,
             AnimatorFactory animatorFactory, Transaction frameTransaction,
             PowerManagerInternal powerManagerInternal) {
-        SurfaceAnimationThread.getHandler().runWithScissors(() -> mChoreographer = getSfInstance(),
+        mSurfaceAnimationHandler.runWithScissors(() -> mChoreographer = getSfInstance(),
                 0 /* timeout */);
         mFrameTransaction = frameTransaction;
         mAnimationHandler = new AnimationHandler();
@@ -152,7 +155,7 @@
                 synchronized (mCancelLock) {
                     anim.mCancelled = true;
                 }
-                SurfaceAnimationThread.getHandler().post(() -> {
+                mSurfaceAnimationHandler.post(() -> {
                     anim.mAnim.cancel();
                     applyTransaction();
                 });
@@ -211,7 +214,7 @@
                         if (!a.mCancelled) {
 
                             // Post on other thread that we can push final state without jank.
-                            AnimationThread.getHandler().post(a.mFinishCallback);
+                            mAnimationThreadHandler.post(a.mFinishCallback);
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 987ecc5..9a140da 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -54,6 +54,7 @@
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.SurfaceControl.METADATA_TASK_ID;
 
 import static com.android.server.am.TaskRecordProto.ACTIVITIES;
@@ -93,6 +94,7 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 
 import static java.lang.Integer.MAX_VALUE;
 
@@ -127,7 +129,6 @@
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.RemoteAnimationTarget;
 import android.view.Surface;
@@ -268,9 +269,6 @@
 
     int mLockTaskUid = -1;  // The uid of the application that called startLockTask().
 
-    /** Current stack. Setter must always be used to update the value. */
-    private ActivityStack mStack;
-
     /** The process that had previously hosted the root activity of this task.
      * Used to know that we should try harder to keep this process around, in case the
      * user wants to return to it. */
@@ -346,7 +344,7 @@
     private final Rect mOverrideDisplayedBounds = new Rect();
 
     /** ID of the display which rotation {@link #mRotation} has. */
-    private int mLastRotationDisplayId = Display.INVALID_DISPLAY;
+    private int mLastRotationDisplayId = INVALID_DISPLAY;
     /**
      * Display rotation as of the last time {@link #setBounds(Rect)} was called or this task was
      * moved to a new display.
@@ -388,6 +386,8 @@
 
     private static Exception sTmpException;
 
+    private boolean mForceShowForAllUsers;
+
     private final FindRootHelper mFindRootHelper = new FindRootHelper();
     private class FindRootHelper {
         private ActivityRecord mRoot;
@@ -678,7 +678,7 @@
         if (toStack == sourceStack) {
             return false;
         }
-        if (!canBeLaunchedOnDisplay(toStack.mDisplayId)) {
+        if (!canBeLaunchedOnDisplay(toStack.getDisplayId())) {
             return false;
         }
 
@@ -959,10 +959,6 @@
         mNextAffiliateTaskId = nextAffiliate == null ? INVALID_TASK_ID : nextAffiliate.mTaskId;
     }
 
-    ActivityStack getStack() {
-        return mStack;
-    }
-
     @Override
     void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
         final ActivityStack oldStack = ((ActivityStack) oldParent);
@@ -973,8 +969,6 @@
             cleanUpResourcesForDestroy();
         }
 
-        mStack = newStack;
-
         super.onParentChanged(newParent, oldParent);
 
         if (oldStack != null) {
@@ -1044,13 +1038,6 @@
         mAtmService.mRootWindowContainer.invalidateTaskLayers();
     }
 
-    /**
-     * @return Id of current stack, {@link ActivityTaskManager#INVALID_STACK_ID} if no stack is set.
-     */
-    int getStackId() {
-        return mStack != null ? mStack.mStackId : INVALID_STACK_ID;
-    }
-
     // Close up recents linked list.
     private void closeRecentsChain() {
         if (mPrevAffiliate != null) {
@@ -1261,7 +1248,7 @@
             }
         } else if (!mReuseTask) {
             // Remove entire task if it doesn't have any activity left and it isn't marked for reuse
-            mStack.removeChild(this, reason);
+            getStack().removeChild(this, reason);
             EventLogTags.writeWmTaskRemoved(mTaskId,
                     "removeChild: last r=" + r + " in t=" + this);
             removeIfPossible();
@@ -1278,9 +1265,9 @@
             return false;
         }
         if (includeFinishing) {
-            return getActivity((r) -> r.mTaskOverlay) != null;
+            return getActivity((r) -> r.isTaskOverlay()) != null;
         }
-        return getActivity((r) -> !r.finishing && r.mTaskOverlay) != null;
+        return getActivity((r) -> !r.finishing && r.isTaskOverlay()) != null;
     }
 
     private boolean autoRemoveFromRecents() {
@@ -1296,7 +1283,7 @@
      */
     private void performClearTaskAtIndexLocked(String reason) {
         // Broken down into to cases to avoid object create due to capturing mStack.
-        if (mStack == null) {
+        if (getStack() == null) {
             forAllActivities((r) -> {
                 if (r.finishing) return;
                 // Task was restored from persistent storage.
@@ -1525,7 +1512,7 @@
 
     private static boolean setTaskDescriptionFromActivityAboveRoot(
             ActivityRecord r, ActivityRecord root, TaskDescription td) {
-        if (!r.mTaskOverlay && r.taskDescription != null) {
+        if (!r.isTaskOverlay() && r.taskDescription != null) {
             final TaskDescription atd = r.taskDescription;
             if (td.getLabel() == null) {
                 td.setLabel(atd.getLabel());
@@ -1579,11 +1566,10 @@
         // If the task has no requested minimal size, we'd like to enforce a minimal size
         // so that the user can not render the task too small to manipulate. We don't need
         // to do this for the pinned stack as the bounds are controlled by the system.
-        if (!inPinnedWindowingMode() && mStack != null) {
+        if (!inPinnedWindowingMode() && getDisplayContent() != null) {
             final int defaultMinSizeDp =
                     mAtmService.mRootWindowContainer.mDefaultMinSizeOfResizeableTaskDp;
-            final DisplayContent display =
-                    mAtmService.mRootWindowContainer.getDisplayContent(mStack.mDisplayId);
+            final DisplayContent display = getDisplayContent();
             final float density =
                     (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
             final int defaultMinSize = (int) (defaultMinSizeDp * density);
@@ -1820,7 +1806,7 @@
      * @param bounds bounds to calculate smallestwidthdp for.
      */
     private int getSmallestScreenWidthDpForDockedBounds(Rect bounds) {
-        DisplayContent dc = mStack.getDisplay().mDisplayContent;
+        DisplayContent dc = getDisplayContent();
         if (dc != null) {
             return dc.getDockedDividerController().getSmallestWidthDpForBounds(bounds);
         }
@@ -1884,9 +1870,9 @@
             if (insideParentBounds && WindowConfiguration.isFloating(windowingMode)) {
                 mTmpNonDecorBounds.set(mTmpFullBounds);
                 mTmpStableBounds.set(mTmpFullBounds);
-            } else if (insideParentBounds && mStack != null) {
+            } else if (insideParentBounds && getDisplayContent() != null) {
                 final DisplayInfo di = new DisplayInfo();
-                mStack.getDisplay().mDisplay.getDisplayInfo(di);
+                getDisplayContent().mDisplay.getDisplayInfo(di);
 
                 // For calculating screenWidthDp, screenWidthDp, we use the stable inset screen
                 // area, i.e. the screen area without the system bars.
@@ -1997,7 +1983,7 @@
                     ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
             final Rect parentBounds =
                     new Rect(newParentConfig.windowConfiguration.getBounds());
-            final DisplayContent display = mStack.getDisplay();
+            final DisplayContent display = getDisplayContent();
             if (display != null && display.mDisplayContent != null) {
                 // If a freeform window moves below system bar, there is no way to move it again
                 // by touch. Because its caption is covered by system bar. So we exclude them
@@ -2077,7 +2063,8 @@
     /** Updates the task's bounds and override configuration to match what is expected for the
      * input stack. */
     void updateOverrideConfigurationForStack(ActivityStack inStack) {
-        if (mStack != null && mStack == inStack) {
+        final ActivityStack stack = getStack();
+        if (stack != null && stack == inStack) {
             return;
         }
 
@@ -2101,7 +2088,8 @@
 
     /** Returns the bounds that should be used to launch this task. */
     Rect getLaunchBounds() {
-        if (mStack == null) {
+        final ActivityStack stack = getStack();
+        if (stack == null) {
             return null;
         }
 
@@ -2109,9 +2097,9 @@
         if (!isActivityTypeStandardOrUndefined()
                 || windowingMode == WINDOWING_MODE_FULLSCREEN
                 || (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
-            return isResizeable() ? mStack.getRequestedOverrideBounds() : null;
+            return isResizeable() ? stack.getRequestedOverrideBounds() : null;
         } else if (!getWindowConfiguration().persistTaskBounds()) {
-            return mStack.getRequestedOverrideBounds();
+            return stack.getRequestedOverrideBounds();
         }
         return mLastNonFullscreenBounds;
     }
@@ -2134,19 +2122,32 @@
 
     @Override
     DisplayContent getDisplayContent() {
-        return getTaskStack() != null ? getTaskStack().getDisplayContent() : null;
+        return getStack() != null ? getStack().getDisplayContent() : null;
     }
 
-    ActivityStack getTaskStack() {
+    int getDisplayId() {
+        final DisplayContent dc = getDisplayContent();
+        return dc != null ? dc.mDisplayId : INVALID_DISPLAY;
+    }
+
+    ActivityStack getStack() {
         return (ActivityStack) getParent();
     }
 
+    /**
+     * @return Id of current stack, {@link ActivityTaskManager#INVALID_STACK_ID} if no stack is set.
+     */
+    int getStackId() {
+        final ActivityStack stack = getStack();
+        return stack != null ? stack.mStackId : INVALID_STACK_ID;
+    }
+
     // TODO(task-hierarchy): Needs to take a generic WindowManager when task contains other tasks.
     int getAdjustedAddPosition(ActivityRecord r, int suggestedPosition) {
         int maxPosition = mChildren.size();
-        if (!r.mTaskOverlay) {
+        if (!r.isTaskOverlay()) {
             // We want to place all non-overlay activities below overlays.
-            final ActivityRecord bottomMostOverlay = getActivity((ar) -> ar.mTaskOverlay, false);
+            final ActivityRecord bottomMostOverlay = getActivity((ar) -> ar.isTaskOverlay(), false);
             if (bottomMostOverlay != null) {
                 maxPosition = Math.max(mChildren.indexOf(bottomMostOverlay) - 1, 0);
             }
@@ -2171,7 +2172,7 @@
             // No reason to defer removal of a Task that doesn't have any child.
             return false;
         }
-        return hasWindowsAlive() && getTaskStack().isAnimating(TRANSITION | CHILDREN);
+        return hasWindowsAlive() && getStack().isAnimating(TRANSITION | CHILDREN);
     }
 
     @Override
@@ -2184,10 +2185,10 @@
     // TODO: Consolidate this with Task.reparent()
     void reparent(ActivityStack stack, int position, boolean moveParents, String reason) {
         if (DEBUG_STACK) Slog.i(TAG, "reParentTask: removing taskId=" + mTaskId
-                + " from stack=" + getTaskStack());
+                + " from stack=" + getStack());
         EventLogTags.writeWmTaskRemoved(mTaskId, "reParentTask:" + reason);
 
-        position = stack.findPositionForTask(this, position, showForAllUsers());
+        position = stack.findPositionForTask(this, position);
 
         reparent(stack, position);
 
@@ -2214,8 +2215,8 @@
     @Override
     public int setBounds(Rect bounds) {
         int rotation = Surface.ROTATION_0;
-        final DisplayContent displayContent = getTaskStack() != null
-                ? getTaskStack().getDisplayContent() : null;
+        final DisplayContent displayContent = getStack() != null
+                ? getStack().getDisplayContent() : null;
         if (displayContent != null) {
             rotation = displayContent.getDisplayInfo().rotation;
         } else if (bounds == null) {
@@ -2256,7 +2257,7 @@
     void onDisplayChanged(DisplayContent dc) {
         adjustBoundsForDisplayChangeIfNeeded(dc);
         super.onDisplayChanged(dc);
-        final int displayId = (dc != null) ? dc.getDisplayId() : Display.INVALID_DISPLAY;
+        final int displayId = (dc != null) ? dc.getDisplayId() : INVALID_DISPLAY;
         mWmService.mAtmService.getTaskChangeNotificationController().notifyTaskDisplayChanged(
                 mTaskId, displayId);
     }
@@ -2401,7 +2402,7 @@
 
     /** Bounds of the task to be used for dimming, as well as touch related tests. */
     public void getDimBounds(Rect out) {
-        final DisplayContent displayContent = getTaskStack().getDisplayContent();
+        final DisplayContent displayContent = getStack().getDisplayContent();
         // It doesn't matter if we in particular are part of the resize, since we couldn't have
         // a DimLayer anyway if we weren't visible.
         final boolean dockedResizing = displayContent != null
@@ -2424,9 +2425,9 @@
             // stack bounds and so we don't even want to use them. Even if the app should not be
             // resized the Dim should keep up with the divider.
             if (dockedResizing) {
-                getTaskStack().getBounds(out);
+                getStack().getBounds(out);
             } else {
-                getTaskStack().getBounds(mTmpRect);
+                getStack().getBounds(mTmpRect);
                 mTmpRect.intersect(getBounds());
                 out.set(mTmpRect);
             }
@@ -2439,9 +2440,9 @@
     void setDragResizing(boolean dragResizing, int dragResizeMode) {
         if (mDragResizing != dragResizing) {
             // No need to check if the mode is allowed if it's leaving dragResize
-            if (dragResizing && !DragResizeMode.isModeAllowedForStack(getTaskStack(), dragResizeMode)) {
+            if (dragResizing && !DragResizeMode.isModeAllowedForStack(getStack(), dragResizeMode)) {
                 throw new IllegalArgumentException("Drag resize mode not allow for stack stackId="
-                        + getTaskStack().mStackId + " dragResizeMode=" + dragResizeMode);
+                        + getStack().mStackId + " dragResizeMode=" + dragResizeMode);
             }
             mDragResizing = dragResizing;
             mDragResizeMode = dragResizeMode;
@@ -2524,6 +2525,15 @@
         return r != null && r.mShowForAllUsers;
     }
 
+    @Override
+    boolean showToCurrentUser() {
+        return mForceShowForAllUsers || showForAllUsers() || mWmService.isCurrentProfile(mUserId);
+    }
+
+    void setForceShowForAllUsers(boolean forceShowForAllUsers) {
+        mForceShowForAllUsers = forceShowForAllUsers;
+    }
+
     /**
      * When we are in a floating stack (Freeform, Pinned, ...) we calculate
      * insets differently. However if we are animating to the fullscreen stack
@@ -2532,7 +2542,7 @@
      */
     boolean isFloating() {
         return getWindowConfiguration().tasksAreFloating()
-                && !getTaskStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
+                && !getStack().isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState;
     }
 
     @Override
@@ -2546,6 +2556,16 @@
         return getAppAnimationLayer(ANIMATION_LAYER_HOME);
     }
 
+    @Override
+    Rect getAnimationBounds(int appStackClipMode) {
+        // TODO(b/131661052): we should remove appStackClipMode with hierarchical animations.
+        if (appStackClipMode == STACK_CLIP_BEFORE_ANIM && getStack() != null) {
+            // Using the stack bounds here effectively applies the clipping before animation.
+            return getStack().getBounds();
+        }
+        return super.getAnimationBounds(appStackClipMode);
+    }
+
     boolean shouldAnimate() {
         // Don't animate while the task runs recents animation but only if we are in the mode
         // where we cancel with deferred screenshot, which means that the controller has
@@ -2559,6 +2579,18 @@
     }
 
     @Override
+    protected void onAnimationFinished() {
+        super.onAnimationFinished();
+        // TODO(b/142617871): we may need to add animation type parameter on onAnimationFinished to
+        //  identify if the callback is for launch animation finish and then calling
+        //  activity#onAnimationFinished.
+        final ActivityRecord activity = getTopMostActivity();
+        if (activity != null) {
+            activity.onAnimationFinished();
+        }
+    }
+
+    @Override
     SurfaceControl.Builder makeSurface() {
         return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
     }
@@ -2585,7 +2617,7 @@
     @Override
     RemoteAnimationTarget createRemoteAnimationTarget(
             RemoteAnimationController.RemoteAnimationRecord record) {
-        final ActivityRecord activity = getTopVisibleActivity();
+        final ActivityRecord activity = getTopMostActivity();
         return activity != null ? activity.createRemoteAnimationTarget(record) : null;
     }
 
@@ -2712,7 +2744,13 @@
         getDimBounds(mTmpDimBoundsRect);
 
         // Bounds need to be relative, as the dim layer is a child.
-        mTmpDimBoundsRect.offsetTo(0, 0);
+        if (inFreeformWindowingMode()) {
+            getBounds(mTmpRect);
+            mTmpDimBoundsRect.offsetTo(mTmpDimBoundsRect.left - mTmpRect.left,
+                    mTmpDimBoundsRect.top - mTmpRect.top);
+        } else {
+            mTmpDimBoundsRect.offsetTo(0, 0);
+        }
         if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
             scheduleAnimation();
         }
@@ -2770,7 +2808,7 @@
         info.userId = mUserId;
         info.stackId = getStackId();
         info.taskId = mTaskId;
-        info.displayId = mStack == null ? Display.INVALID_DISPLAY : mStack.mDisplayId;
+        info.displayId = getDisplayId();
         info.isRunning = getTopNonFinishingActivity() != null;
         info.baseIntent = new Intent(getBaseIntent());
         info.baseActivity = mReuseActivitiesReport.base != null
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index b8f3abe..e6757e1 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -316,8 +316,8 @@
         ActivityStack stack =
                 (displayId == INVALID_DISPLAY && task != null) ? task.getStack() : null;
         if (stack != null) {
-            if (DEBUG) appendLog("display-from-task=" + stack.mDisplayId);
-            displayId = stack.mDisplayId;
+            if (DEBUG) appendLog("display-from-task=" + stack.getDisplayId());
+            displayId = stack.getDisplayId();
         }
 
         if (displayId == INVALID_DISPLAY && source != null) {
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index 8cff99b..20af250 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -339,7 +339,7 @@
                                             + userTasksDir.getAbsolutePath());
                                 } else {
                                     // Looks fine.
-                                    mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
+                                    mStackSupervisor.setNextTaskIdForUser(taskId, userId);
                                     task.isPersistable = true;
                                     tasks.add(task);
                                     recoveredTaskIds.add(taskId);
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index a867a5d4..8bbb0d7 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -274,7 +274,6 @@
         mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, display.getDisplayId());
         mDragWindowHandle.name = TAG;
         mDragWindowHandle.token = mServerChannel.getToken();
-        mDragWindowHandle.layer = mService.getDragLayerLocked();
         mDragWindowHandle.layoutParamsFlags = 0;
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
         mDragWindowHandle.dispatchingTimeoutNanos =
@@ -450,7 +449,7 @@
         }
 
         // This is a moving or scrolling operation.
-        mTask.getTaskStack().getDimBounds(mTmpRect);
+        mTask.getStack().getDimBounds(mTmpRect);
         // If a target window is covered by system bar, there is no way to move it again by touch.
         // So we exclude them from stack bounds. and then it will be shown inside stable area.
         Rect stableBounds = new Rect();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index e4744db..4cb5de4 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -470,7 +470,7 @@
         final LayoutParams attrs = mainWindow.getAttrs();
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
-                mFullSnapshotScale, mainWindow.getClientInsetsState());
+                mFullSnapshotScale, mainWindow.getRequestedInsetsState());
         final int width = (int) (task.getBounds().width() * mFullSnapshotScale);
         final int height = (int) (task.getBounds().height() * mFullSnapshotScale);
 
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 5b458d8..e2a21a9 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -222,7 +222,7 @@
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
                 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
                 windowFlags, windowPrivateFlags, taskBounds,
-                currentOrientation, topFullscreenOpaqueWindow.getClientInsetsState());
+                currentOrientation, topFullscreenOpaqueWindow.getRequestedInsetsState());
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6ff4b2e..137d122 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -121,7 +121,7 @@
 
         mFindResults.resetTopWallpaper = true;
         if (w.mActivityRecord != null && !w.mActivityRecord.isVisible()
-                && !w.mActivityRecord.isAnimating(TRANSITION)) {
+                && !w.mActivityRecord.isAnimating(TRANSITION | PARENTS)) {
 
             // If this window's app token is hidden and not animating, it is of no interest to us.
             if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w);
@@ -139,7 +139,7 @@
         }
 
         final boolean keyguardGoingAwayWithWallpaper = (w.mActivityRecord != null
-                && w.mActivityRecord.isAnimating(TRANSITION)
+                && w.mActivityRecord.isAnimating(TRANSITION | PARENTS)
                 && AppTransition.isKeyguardGoingAwayTransit(w.mActivityRecord.getTransit())
                 && (w.mActivityRecord.getTransitFlags()
                         & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER) != 0);
@@ -162,9 +162,11 @@
 
         final RecentsAnimationController recentsAnimationController =
                 mService.getRecentsAnimationController();
-        final boolean animationWallpaper = w.mActivityRecord != null
-                && w.mActivityRecord.getAnimation() != null
-                && w.mActivityRecord.getAnimation().getShowWallpaper();
+        final WindowContainer animatingContainer =
+                w.mActivityRecord != null ? w.mActivityRecord.getAnimatingContainer() : null;
+        final boolean animationWallpaper = animatingContainer != null
+                && animatingContainer.getAnimation() != null
+                && animatingContainer.getAnimation().getShowWallpaper();
         final boolean hasWallpaper = (w.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
                 || animationWallpaper;
         final boolean isRecentsTransitionTarget = (recentsAnimationController != null
@@ -228,14 +230,14 @@
         if (DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + ", obscured="
                 + (wallpaperTarget != null ? Boolean.toString(wallpaperTarget.mObscured) : "??")
                 + " animating=" + ((wallpaperTarget != null && wallpaperTarget.mActivityRecord != null)
-                ? wallpaperTarget.mActivityRecord.isAnimating(TRANSITION) : null)
+                ? wallpaperTarget.mActivityRecord.isAnimating(TRANSITION | PARENTS) : null)
                 + " prev=" + mPrevWallpaperTarget
                 + " recentsAnimationWallpaperVisible=" + isAnimatingWithRecentsComponent);
         return (wallpaperTarget != null
                 && (!wallpaperTarget.mObscured
                         || isAnimatingWithRecentsComponent
                         || (wallpaperTarget.mActivityRecord != null
-                        && wallpaperTarget.mActivityRecord.isAnimating(TRANSITION))))
+                        && wallpaperTarget.mActivityRecord.isAnimating(TRANSITION | PARENTS))))
                 || mPrevWallpaperTarget != null;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f7525a9..fd91bc5 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -32,7 +32,6 @@
 import android.view.Choreographer;
 import android.view.SurfaceControl;
 
-import com.android.server.AnimationThread;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.protolog.common.ProtoLog;
 
@@ -92,7 +91,7 @@
         mContext = service.mContext;
         mPolicy = service.mPolicy;
         mTransaction = service.mTransactionFactory.get();
-        AnimationThread.getHandler().runWithScissors(
+        service.mAnimationHandler.runWithScissors(
                 () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
 
         mAnimationFrameCallback = frameTimeNs -> {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5c02f46..cefef37 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -777,7 +777,7 @@
      *         otherwise.
      */
     boolean isWaitingForTransitionStart() {
-        return false;
+        return getActivity(app -> app.isWaitingForTransitionStart()) != null;
     }
 
     /**
@@ -785,7 +785,7 @@
      *         {@code ActivityRecord#isAnimating(TRANSITION)}, {@code false} otherwise.
      */
     boolean isAppTransitioning() {
-        return getActivity(app -> app.isAnimating(TRANSITION)) != null;
+        return getActivity(app -> app.isAnimating(PARENTS | TRANSITION)) != null;
     }
 
     /**
@@ -1071,6 +1071,10 @@
         }
     }
 
+    boolean showToCurrentUser() {
+        return true;
+    }
+
     /**
      * For all windows at or below this container call the callback.
      * @param   callback Calls the {@link ToBooleanFunction#apply} method for each window found and
@@ -1323,12 +1327,12 @@
             if (includeOverlays) {
                 return getActivity((r) -> true);
             }
-            return getActivity((r) -> !r.mTaskOverlay);
+            return getActivity((r) -> !r.isTaskOverlay());
         } else if (includeOverlays) {
             return getActivity((r) -> !r.finishing);
         }
 
-        return getActivity((r) -> !r.finishing && !r.mTaskOverlay);
+        return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
     }
 
     void forAllWallpaperWindows(Consumer<WallpaperWindowToken> callback) {
@@ -1891,7 +1895,7 @@
                 if (adapter != null) {
                     startAnimation(getPendingTransaction(), adapter, !isVisible());
                     if (adapter.getShowWallpaper()) {
-                        mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
+                        getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                     }
                     if (thumbnailAdapter != null) {
                         mThumbnail.startAnimation(
@@ -2036,7 +2040,8 @@
     }
 
     boolean okToDisplay() {
-        return mDisplayContent != null && mDisplayContent.okToDisplay();
+        final DisplayContent dc = getDisplayContent();
+        return dc != null && dc.okToDisplay();
     }
 
     boolean okToAnimate() {
@@ -2044,7 +2049,8 @@
     }
 
     boolean okToAnimate(boolean ignoreFrozen) {
-        return mDisplayContent != null && mDisplayContent.okToAnimate(ignoreFrozen);
+        final DisplayContent dc = getDisplayContent();
+        return dc != null && dc.okToAnimate(ignoreFrozen);
     }
 
     @Override
@@ -2086,6 +2092,21 @@
     }
 
     /**
+     * @return The {@link WindowContainer} which is running an animation.
+     *
+     * It traverses from the current container to its parents recursively. If nothing is animating,
+     * it will return {@code null}.
+     */
+    @Nullable
+    WindowContainer getAnimatingContainer() {
+        if (isAnimating()) {
+            return this;
+        }
+        final WindowContainer parent = getParent();
+        return (parent != null) ? parent.getAnimatingContainer() : null;
+    }
+
+    /**
      * @see SurfaceAnimator#startDelayingAnimationStart
      */
     void startDelayingAnimationStart() {
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index 74d5c04..b0c5dbc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -19,8 +19,6 @@
 import static android.provider.AndroidDeviceConfig.KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE;
 import static android.provider.AndroidDeviceConfig.KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP;
 
-import static com.android.internal.util.Preconditions.checkNotNull;
-
 import android.provider.AndroidDeviceConfig;
 import android.provider.DeviceConfig;
 
@@ -28,6 +26,7 @@
 import com.android.server.wm.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -74,8 +73,8 @@
     WindowManagerConstants(WindowManagerGlobalLock globalLock,
             Runnable updateSystemGestureExclusionCallback,
             DeviceConfigInterface deviceConfig) {
-        mGlobalLock = checkNotNull(globalLock);
-        mUpdateSystemGestureExclusionCallback = checkNotNull(updateSystemGestureExclusionCallback);
+        mGlobalLock = Objects.requireNonNull(globalLock);
+        mUpdateSystemGestureExclusionCallback = Objects.requireNonNull(updateSystemGestureExclusionCallback);
         mDeviceConfig = deviceConfig;
         mListenerAndroid = this::onAndroidPropertiesChanged;
         mListenerWindowManager = this::onWindowPropertiesChanged;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ab3419c..223e9b9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -59,7 +59,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
@@ -256,7 +255,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.LatencyTracker;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.internal.view.WindowManagerPolicyThread;
@@ -297,6 +295,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
@@ -630,10 +629,6 @@
 
     boolean mDisableTransitionAnimation;
 
-    int getDragLayerLocked() {
-        return mPolicy.getWindowLayerFromTypeLw(TYPE_DRAG) * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
-    }
-
     class RotationWatcher {
         final IRotationWatcher mWatcher;
         final IBinder.DeathRecipient mDeathRecipient;
@@ -1512,7 +1507,6 @@
             final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
             displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
                     Binder.getCallingUid());
-            win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
 
             res = displayPolicy.validateAddingWindowLw(attrs);
             if (res != WindowManagerGlobal.ADD_OKAY) {
@@ -2869,7 +2863,7 @@
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
-        Preconditions.checkNotNull(token, "token is null");
+        Objects.requireNonNull(token, "token is null");
         final int callingUid = Binder.getCallingUid();
         final long origIdentity = Binder.clearCallingIdentity();
         try {
@@ -3179,7 +3173,7 @@
     }
 
     /* Called by WindowState */
-    boolean isCurrentProfileLocked(int userId) {
+    boolean isCurrentProfile(int userId) {
         if (userId == mCurrentUserId) return true;
         for (int i = 0; i < mCurrentProfileIds.length; i++) {
             if (mCurrentProfileIds[i] == userId) return true;
@@ -4345,7 +4339,7 @@
         final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
         final ActivityRecord focusedApp = topFocusedDisplay.mFocusedApp;
         return (focusedApp != null && focusedApp.getTask() != null)
-                ? focusedApp.getTask().getTaskStack() : null;
+                ? focusedApp.getTask().getStack() : null;
     }
 
     public boolean detectSafeMode() {
@@ -7664,7 +7658,7 @@
             return;
         }
 
-        final ActivityStack stack = task.getTaskStack();
+        final ActivityStack stack = task.getStack();
         // We ignore home stack since we don't want home stack to move to front when touched.
         // Specifically, in freeform we don't want tapping on home to cause the freeform apps to go
         // behind home. See b/117376413
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bb7c26c..4e768da 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -56,19 +56,35 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
+import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
+import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.isSystemAlertWindowType;
 import static android.view.WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_DOCKED;
@@ -194,6 +210,7 @@
 import android.view.InputEvent;
 import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
+import android.view.InsetsSource;
 import android.view.InsetsState;
 import android.view.Surface.Rotation;
 import android.view.SurfaceControl;
@@ -523,9 +540,6 @@
 
     boolean mHasSurface = false;
 
-    /** When true this window can be displayed on screens owther than mOwnerUid's */
-    private boolean mShowToOwnerOnly;
-
     // This window will be replaced due to relaunch. This allows window manager
     // to differentiate between simple removal of a window and replacement. In the latter case it
     // will preserve the old window until the new one is drawn.
@@ -637,17 +651,29 @@
     private boolean mIsDimming = false;
 
     private @Nullable InsetsSourceProvider mControllableInsetProvider;
-    private InsetsState mClientInsetsState;
+    private InsetsState mRequestedInsetsState;
 
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
     private KeyInterceptionInfo mKeyInterceptionInfo;
 
-    InsetsState getClientInsetsState() {
-        return mClientInsetsState;
+    /**
+     * @return The insets state as requested by the client, i.e. the dispatched insets state
+     *         for which the visibilities are overridden with what the client requested.
+     */
+    InsetsState getRequestedInsetsState() {
+        return mRequestedInsetsState;
     }
 
-    void setClientInsetsState(InsetsState state) {
-        mClientInsetsState = state;
+    /**
+     * @see #getRequestedInsetsState()
+     */
+    void updateRequestedInsetsState(InsetsState state) {
+
+        // Only update the sources the client is actually controlling.
+        for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
+            final InsetsSource source = state.sourceAt(i);
+            mRequestedInsetsState.addSource(source);
+        }
     }
 
     void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@@ -766,7 +792,7 @@
         mSeq = seq;
         mPowerManagerWrapper = powerManagerWrapper;
         mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
-        mClientInsetsState =
+        mRequestedInsetsState =
                 getDisplayContent().getInsetsPolicy().getInsetsForDispatch(this);
         if (DEBUG) {
             Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
@@ -958,8 +984,11 @@
         final int layoutXDiff;
         final int layoutYDiff;
         final WindowState imeWin = mWmService.mRoot.getCurrentInputMethodWindow();
+        final boolean isInputMethodAdjustTarget = windowsAreFloating
+                ? dc.mInputMethodTarget != null && task == dc.mInputMethodTarget.getTask()
+                : isInputMethodTarget();
         final boolean isImeTarget =
-                imeWin != null && imeWin.isVisibleNow() && isInputMethodTarget();
+                imeWin != null && imeWin.isVisibleNow() && isInputMethodAdjustTarget;
         if (isFullscreenAndFillsDisplay || layoutInParentFrame()) {
             // We use the parent frame as the containing frame for fullscreen and child windows
             mWindowFrames.mContainingFrame.set(mWindowFrames.mParentFrame);
@@ -990,7 +1019,8 @@
                         final int distanceToTop = Math.max(mWindowFrames.mContainingFrame.top
                                 - mWindowFrames.mContentFrame.top, 0);
                         int offs = Math.min(bottomOverlap, distanceToTop);
-                        mWindowFrames.mContainingFrame.top -= offs;
+                        mWindowFrames.mContainingFrame.offset(0, -offs);
+                        mInsetFrame.offset(0, -offs);
                     }
                 } else if (!inPinnedWindowingMode() && mWindowFrames.mContainingFrame.bottom
                         > mWindowFrames.mParentFrame.bottom) {
@@ -1287,7 +1317,7 @@
         // notify the client of frame changes in this case. Not only is it a lot of churn, but
         // the frame may not correspond to the surface size or the onscreen area at various
         // phases in the animation, and the client will become sad and confused.
-        if (task != null && task.getTaskStack().isAnimatingBounds()) {
+        if (task != null && task.getStack().isAnimatingBounds()) {
             return;
         }
 
@@ -1427,8 +1457,8 @@
     ActivityStack getStack() {
         Task task = getTask();
         if (task != null) {
-            if (task.getTaskStack() != null) {
-                return task.getTaskStack();
+            if (task.getStack() != null) {
+                return task.getStack();
             }
         }
         // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
@@ -1447,7 +1477,7 @@
         bounds.setEmpty();
         mTmpRect.setEmpty();
         if (intersectWithStackBounds) {
-            final ActivityStack stack = task.getTaskStack();
+            final ActivityStack stack = task.getStack();
             if (stack != null) {
                 stack.getDimBounds(mTmpRect);
             } else {
@@ -1560,7 +1590,7 @@
     // TODO: Can we consolidate this with #isVisible() or have a more appropriate name for this?
     boolean isWinVisibleLw() {
         return (mActivityRecord == null || mActivityRecord.mVisibleRequested
-                || mActivityRecord.isAnimating(TRANSITION)) && isVisible();
+                || mActivityRecord.isAnimating(TRANSITION | PARENTS)) && isVisible();
     }
 
     /**
@@ -1786,7 +1816,7 @@
             // Starting window that's exiting will be removed when the animation finishes.
             // Mark all relevant flags for that onExitAnimationDone will proceed all the way
             // to actually remove it.
-            if (!visible && isVisibleNow && mActivityRecord.isAnimating(TRANSITION)) {
+            if (!visible && isVisibleNow && mActivityRecord.isAnimating(PARENTS | TRANSITION)) {
                 mAnimatingExit = true;
                 mRemoveOnExit = true;
                 mWindowRemovalAllowed = true;
@@ -1879,8 +1909,8 @@
         final int top = mWindowFrames.mFrame.top;
         final Task task = getTask();
         final boolean adjustedForMinimizedDockOrIme = task != null
-                && (task.getTaskStack().isAdjustedForMinimizedDockedStack()
-                || task.getTaskStack().isAdjustedForIme());
+                && (task.getStack().isAdjustedForMinimizedDockedStack()
+                || task.getStack().isAdjustedForIme());
         if (mToken.okToAnimate()
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                 && !isDragResizing() && !adjustedForMinimizedDockOrIme
@@ -1916,7 +1946,7 @@
 
     boolean isObscuringDisplay() {
         Task task = getTask();
-        if (task != null && task.getTaskStack() != null && !task.getTaskStack().fillsParent()) {
+        if (task != null && task.getStack() != null && !task.getStack().fillsParent()) {
             return false;
         }
         return isOpaqueDrawn() && fillsDisplay();
@@ -2055,7 +2085,7 @@
                     this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
                     mHasSurface, mWinAnimator.getShown(),
                     isAnimating(TRANSITION | PARENTS),
-                    mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION),
+                    mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
                     mWillReplaceWindow,
                     mWmService.mDisplayFrozen, Debug.getCallers(6));
 
@@ -2349,20 +2379,21 @@
 
     void applyAdjustForImeIfNeeded() {
         final Task task = getTask();
-        if (task != null && task.getTaskStack() != null && task.getTaskStack().isAdjustedForIme()) {
-            task.getTaskStack().applyAdjustForImeIfNeeded(task);
+        if (task != null && task.getStack() != null && task.getStack().isAdjustedForIme()) {
+            task.getStack().applyAdjustForImeIfNeeded(task);
         }
     }
 
     @Override
     void switchUser(int userId) {
         super.switchUser(userId);
-        if (isHiddenFromUserLocked()) {
+
+        if (showToCurrentUser()) {
+            setPolicyVisibilityFlag(VISIBLE_FOR_USER);
+        } else {
             if (DEBUG_VISIBILITY) Slog.w(TAG_WM, "user changing, hiding " + this
                     + ", attrs=" + mAttrs.type + ", belonging to " + mOwnerUid);
             clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
-        } else {
-            setPolicyVisibilityFlag(VISIBLE_FOR_USER);
         }
     }
 
@@ -2694,7 +2725,7 @@
             return false;
         }
 
-        return mActivityRecord.getTask().getTaskStack().shouldIgnoreInput()
+        return mActivityRecord.getTask().getStack().shouldIgnoreInput()
                 || !mActivityRecord.mVisibleRequested
                 || isRecentsAnimationConsumingAppInput();
     }
@@ -2725,7 +2756,7 @@
             // Already showing.
             return false;
         }
-        if (isHiddenFromUserLocked()) {
+        if (!showToCurrentUser()) {
             return false;
         }
         if (!mAppOpVisibility) {
@@ -3133,11 +3164,55 @@
         return displayContent.isDefaultDisplay;
     }
 
-    void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
-        mShowToOwnerOnly = showToOwnerOnly;
+    /** @return {@code true} if this window can be shown to all users. */
+    boolean showForAllUsers() {
+
+        // If this switch statement is modified, modify the comment in the declarations of
+        // the type in {@link WindowManager.LayoutParams} as well.
+        switch (mAttrs.type) {
+            default:
+                // These are the windows that by default are shown only to the user that created
+                // them. If this needs to be overridden, set
+                // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
+                // {@link WindowManager.LayoutParams}. Note that permission
+                // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
+                if ((mAttrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
+                    return false;
+                }
+                break;
+
+            // These are the windows that by default are shown to all users. However, to
+            // protect against spoofing, check permissions below.
+            case TYPE_APPLICATION_STARTING:
+            case TYPE_BOOT_PROGRESS:
+            case TYPE_DISPLAY_OVERLAY:
+            case TYPE_INPUT_CONSUMER:
+            case TYPE_KEYGUARD_DIALOG:
+            case TYPE_MAGNIFICATION_OVERLAY:
+            case TYPE_NAVIGATION_BAR:
+            case TYPE_NAVIGATION_BAR_PANEL:
+            case TYPE_PHONE:
+            case TYPE_POINTER:
+            case TYPE_PRIORITY_PHONE:
+            case TYPE_SEARCH_BAR:
+            case TYPE_STATUS_BAR:
+            case TYPE_STATUS_BAR_PANEL:
+            case TYPE_STATUS_BAR_SUB_PANEL:
+            case TYPE_SYSTEM_DIALOG:
+            case TYPE_VOLUME_OVERLAY:
+            case TYPE_PRESENTATION:
+            case TYPE_PRIVATE_PRESENTATION:
+            case TYPE_DOCK_DIVIDER:
+                break;
+        }
+
+        // Only the system can show free windows to all users.
+        return mOwnerCanAddInternalSystemWindow;
+
     }
 
-    private boolean isHiddenFromUserLocked() {
+    @Override
+    boolean showToCurrentUser() {
         // Child windows are evaluated based on their parent window.
         final WindowState win = getTopParentWindow();
         if (win.mAttrs.type < FIRST_SYSTEM_WINDOW
@@ -3151,12 +3226,12 @@
                     && win.getFrameLw().right >= win.getStableFrameLw().right
                     && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
                 // Is a fullscreen window, like the clock alarm. Show to everyone.
-                return false;
+                return true;
             }
         }
 
-        return win.mShowToOwnerOnly
-                && !mWmService.isCurrentProfileLocked(UserHandle.getUserId(win.mOwnerUid));
+        return win.showForAllUsers()
+                || mWmService.isCurrentProfile(UserHandle.getUserId(win.mOwnerUid));
     }
 
     private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
@@ -3214,7 +3289,7 @@
             return;
         }
 
-        final ActivityStack stack = task.getTaskStack();
+        final ActivityStack stack = task.getStack();
         if (stack == null) {
             return;
         }
@@ -3228,7 +3303,7 @@
             return;
         }
 
-        final ActivityStack stack = task.getTaskStack();
+        final ActivityStack stack = task.getStack();
         if (stack == null) {
             return;
         }
@@ -3716,7 +3791,7 @@
         pw.println(" mSession=" + mSession
                 + " mClient=" + mClient.asBinder());
         pw.println(prefix + "mOwnerUid=" + mOwnerUid
-                + " mShowToOwnerOnly=" + mShowToOwnerOnly
+                + " showForAllUsers=" + showForAllUsers()
                 + " package=" + mAttrs.packageName
                 + " appop=" + AppOpsManager.opToName(mAppOp));
         pw.println(prefix + "mAttrs=" + mAttrs.toString(prefix));
@@ -4160,7 +4235,7 @@
 
     // This must be called while inside a transaction.
     boolean performShowLocked() {
-        if (isHiddenFromUserLocked()) {
+        if (!showToCurrentUser()) {
             if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + this + ", belonging to " + mOwnerUid);
             clearPolicyVisibilityFlag(VISIBLE_FOR_USER);
             return false;
@@ -4229,7 +4304,7 @@
                     + " tok.visible=" + (mActivityRecord != null && mActivityRecord.isVisible())
                     + " animating=" + isAnimating(TRANSITION | PARENTS)
                     + " tok animating="
-                    + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION))
+                    + (mActivityRecord != null && mActivityRecord.isAnimating(TRANSITION | PARENTS))
                     + " Callers=" + Debug.getCallers(4));
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 175fccb..486616d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -888,10 +888,10 @@
 
             int posX = 0;
             int posY = 0;
-            task.getTaskStack().getDimBounds(mTmpStackBounds);
+            task.getStack().getDimBounds(mTmpStackBounds);
 
             boolean allowStretching = false;
-            task.getTaskStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
+            task.getStack().getFinalAnimationSourceHintBounds(mTmpSourceBounds);
             // If we don't have source bounds, we can attempt to use the content insets
             // in the following scenario:
             //    1. We have content insets.
@@ -901,8 +901,8 @@
             // because of the force-scale until resize state.
             if (mTmpSourceBounds.isEmpty() && (mWin.mLastRelayoutContentInsets.width() > 0
                     || mWin.mLastRelayoutContentInsets.height() > 0)
-                        && !task.getTaskStack().lastAnimatingBoundsWasToFullscreen()) {
-                mTmpSourceBounds.set(task.getTaskStack().mPreAnimationBounds);
+                        && !task.getStack().lastAnimatingBoundsWasToFullscreen()) {
+                mTmpSourceBounds.set(task.getStack().mPreAnimationBounds);
                 mTmpSourceBounds.inset(mWin.mLastRelayoutContentInsets);
                 allowStretching = true;
             }
@@ -916,7 +916,7 @@
             if (!mTmpSourceBounds.isEmpty()) {
                 // Get the final target stack bounds, if we are not animating, this is just the
                 // current stack bounds
-                task.getTaskStack().getFinalAnimationBounds(mTmpAnimatingBounds);
+                task.getStack().getFinalAnimationBounds(mTmpAnimatingBounds);
 
                 // Calculate the current progress and interpolate the difference between the target
                 // and source bounds
@@ -1398,7 +1398,7 @@
             mWin.getDisplayContent().adjustForImeIfNeeded();
         }
 
-        return mWin.isAnimating(TRANSITION | PARENTS);
+        return mWin.isAnimating(PARENTS);
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1495,7 +1495,7 @@
      */
     boolean isForceScaled() {
         final Task task = mWin.getTask();
-        if (task != null && task.getTaskStack().isForceScaled()) {
+        if (task != null && task.getStack().isForceScaled()) {
             return true;
         }
         return mForceScaleUntilResize;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index fee29db..1ad6e86 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -138,6 +138,7 @@
         "android.hardware.thermal@1.0",
         "android.hardware.tv.cec@1.0",
         "android.hardware.tv.input@1.0",
+        "android.hardware.vibrator-cpp",
         "android.hardware.vibrator@1.0",
         "android.hardware.vibrator@1.1",
         "android.hardware.vibrator@1.2",
@@ -148,7 +149,6 @@
         "android.system.suspend@1.0",
         "service.incremental",
         "suspend_control_aidl_interface-cpp",
-        "vintf-vibrator-cpp",
     ],
 
     static_libs: [
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 4d4a7b4..4696dd0 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -54,7 +54,7 @@
     TCP_TX_PACKETS = 5
 };
 
-static uint64_t getStatsType(struct Stats* stats, StatsType type) {
+static uint64_t getStatsType(Stats* stats, StatsType type) {
     switch (type) {
         case RX_BYTES:
             return stats->rxBytes;
@@ -73,7 +73,7 @@
     }
 }
 
-static int parseIfaceStats(const char* iface, struct Stats* stats) {
+static int parseIfaceStats(const char* iface, Stats* stats) {
     FILE *fp = fopen(QTAGUID_IFACE_STATS, "r");
     if (fp == NULL) {
         return -1;
@@ -117,7 +117,7 @@
     return 0;
 }
 
-static int parseUidStats(const uint32_t uid, struct Stats* stats) {
+static int parseUidStats(const uint32_t uid, Stats* stats) {
     FILE *fp = fopen(QTAGUID_UID_STATS, "r");
     if (fp == NULL) {
         return -1;
@@ -150,8 +150,7 @@
 }
 
 static jlong getTotalStat(JNIEnv* env, jclass clazz, jint type, jboolean useBpfStats) {
-    struct Stats stats;
-    memset(&stats, 0, sizeof(Stats));
+    Stats stats = {};
 
     if (useBpfStats) {
         if (bpfGetIfaceStats(NULL, &stats) == 0) {
@@ -175,8 +174,7 @@
         return UNKNOWN;
     }
 
-    struct Stats stats;
-    memset(&stats, 0, sizeof(Stats));
+    Stats stats = {};
 
     if (useBpfStats) {
         if (bpfGetIfaceStats(iface8.c_str(), &stats) == 0) {
@@ -194,8 +192,7 @@
 }
 
 static jlong getUidStat(JNIEnv* env, jclass clazz, jint uid, jint type, jboolean useBpfStats) {
-    struct Stats stats;
-    memset(&stats, 0, sizeof(Stats));
+    Stats stats = {};
 
     if (useBpfStats) {
         if (bpfGetUidStats(uid, &stats) == 0) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1536816..13246ba 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -215,6 +215,7 @@
 import android.provider.ContactsInternal;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.provider.Telephony;
 import android.security.IKeyChainAliasCallback;
 import android.security.IKeyChainService;
 import android.security.KeyChain;
@@ -303,6 +304,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -365,6 +367,8 @@
 
     private static final String TAG_TRANSFER_OWNERSHIP_BUNDLE = "transfer-ownership-bundle";
 
+    private static final String TAG_PROTECTED_PACKAGES = "protected-packages";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = TimeUnit.DAYS.toMillis(1);
@@ -740,6 +744,9 @@
         // This is the list of component allowed to start lock task mode.
         List<String> mLockTaskPackages = new ArrayList<>();
 
+        // List of packages protected by device owner
+        List<String> mProtectedPackages = new ArrayList<>();
+
         // Bitfield of feature flags to be enabled during LockTask mode.
         // We default on the power button menu, in order to be consistent with pre-P behaviour.
         int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
@@ -2266,23 +2273,23 @@
     @VisibleForTesting
     DevicePolicyManagerService(Injector injector) {
         mInjector = injector;
-        mContext = Preconditions.checkNotNull(injector.mContext);
-        mHandler = new Handler(Preconditions.checkNotNull(injector.getMyLooper()));
+        mContext = Objects.requireNonNull(injector.mContext);
+        mHandler = new Handler(Objects.requireNonNull(injector.getMyLooper()));
 
         mConstantsObserver = new DevicePolicyConstantsObserver(mHandler);
         mConstantsObserver.register();
         mConstants = loadConstants();
 
-        mOwners = Preconditions.checkNotNull(injector.newOwners());
+        mOwners = Objects.requireNonNull(injector.newOwners());
 
-        mUserManager = Preconditions.checkNotNull(injector.getUserManager());
-        mUserManagerInternal = Preconditions.checkNotNull(injector.getUserManagerInternal());
-        mUsageStatsManagerInternal = Preconditions.checkNotNull(
+        mUserManager = Objects.requireNonNull(injector.getUserManager());
+        mUserManagerInternal = Objects.requireNonNull(injector.getUserManagerInternal());
+        mUsageStatsManagerInternal = Objects.requireNonNull(
                 injector.getUsageStatsManagerInternal());
-        mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
-        mIPlatformCompat = Preconditions.checkNotNull(injector.getIPlatformCompat());
-        mIPermissionManager = Preconditions.checkNotNull(injector.getIPermissionManager());
-        mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager());
+        mIPackageManager = Objects.requireNonNull(injector.getIPackageManager());
+        mIPlatformCompat = Objects.requireNonNull(injector.getIPlatformCompat());
+        mIPermissionManager = Objects.requireNonNull(injector.getIPermissionManager());
+        mTelephonyManager = Objects.requireNonNull(injector.getTelephonyManager());
 
         mLocalService = new LocalService();
         mLockPatternUtils = injector.newLockPatternUtils();
@@ -3243,6 +3250,13 @@
                 out.endTag(null, TAG_OWNER_INSTALLED_CA_CERT);
             }
 
+            for (int i = 0, size = policy.mProtectedPackages.size(); i < size; i++) {
+                String packageName = policy.mProtectedPackages.get(i);
+                out.startTag(null, TAG_PROTECTED_PACKAGES);
+                out.attribute(null, ATTR_NAME, packageName);
+                out.endTag(null, TAG_PROTECTED_PACKAGES);
+            }
+
             out.endTag(null, "policies");
 
             out.endDocument();
@@ -3356,6 +3370,7 @@
             policy.mAdminMap.clear();
             policy.mAffiliationIds.clear();
             policy.mOwnerInstalledCaCerts.clear();
+            policy.mProtectedPackages.clear();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -3453,6 +3468,8 @@
                     policy.mCurrentInputMethodSet = true;
                 } else if (TAG_OWNER_INSTALLED_CA_CERT.equals(tag)) {
                     policy.mOwnerInstalledCaCerts.add(parser.getAttributeValue(null, ATTR_ALIAS));
+                } else if (TAG_PROTECTED_PACKAGES.equals(tag)) {
+                    policy.mProtectedPackages.add(parser.getAttributeValue(null, ATTR_NAME));
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -3484,6 +3501,7 @@
         updateMaximumTimeToLockLocked(userHandle);
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
         updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
+        updateProtectedPackagesLocked(policy.mProtectedPackages);
         if (policy.mStatusBarDisabled) {
             setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
         }
@@ -3509,6 +3527,10 @@
         }
     }
 
+    private void updateProtectedPackagesLocked(List<String> packages) {
+        mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages);
+    }
+
     private void updateLockTaskFeaturesLocked(int flags, int userId) {
         long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -4047,7 +4069,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(adminReceiver, "ComponentName is null");
+        Objects.requireNonNull(adminReceiver, "ComponentName is null");
         enforceShell("forceRemoveActiveAdmin");
         mInjector.binderWithCleanCallingIdentity(() -> {
             synchronized (getLockObject()) {
@@ -4164,7 +4186,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         validateQualityConstant(quality);
 
         final int userId = mInjector.userHandleGetCallingUserId();
@@ -4377,7 +4399,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4419,7 +4441,7 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4451,7 +4473,7 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkArgumentNonnegative(timeout, "Timeout must be >= 0 ms");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
@@ -4633,7 +4655,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             final ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4663,7 +4685,7 @@
 
     @Override
     public void setPasswordMinimumLowerCase(ComponentName who, int length, boolean parent) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4696,7 +4718,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4728,7 +4750,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4760,7 +4782,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -4792,7 +4814,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -5019,7 +5041,7 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             // This API can only be called by an active device admin,
@@ -5259,7 +5281,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             final ActiveAdmin ap = getActiveAdminForCallerLocked(
@@ -5360,7 +5382,7 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number.");
         // timeoutMs with value 0 means that the admin doesn't participate
         // timeoutMs is clamped to the interval in case the internal constants change in the future
@@ -5805,8 +5827,9 @@
             // Make sure that the caller is the profile owner or delegate.
             enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
                     DELEGATION_CERT_INSTALL);
-            // Verify that the profile owner was granted access to Device IDs.
-            if (canProfileOwnerAccessDeviceIds(userId)) {
+            // Verify that the managed profile is on an organization-owned device and as such
+            // the profile owner can access Device IDs.
+            if (isProfileOwnerOfOrganizationOwnedDevice(userId)) {
                 return;
             }
             throw new SecurityException(
@@ -6108,7 +6131,7 @@
     @Override
     public void setDelegatedScopes(ComponentName who, String delegatePackage,
             List<String> scopeList) throws SecurityException {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(delegatePackage, "Delegate package is null or empty");
         Preconditions.checkCollectionElementsNotNull(scopeList, "Scopes");
         // Remove possible duplicates.
@@ -6206,7 +6229,7 @@
     @NonNull
     public List<String> getDelegatedScopes(ComponentName who,
             String delegatePackage) throws SecurityException {
-        Preconditions.checkNotNull(delegatePackage, "Delegate package is null");
+        Objects.requireNonNull(delegatePackage, "Delegate package is null");
 
         // Retrieve the user ID of the calling process.
         final int callingUid = mInjector.binderGetCallingUid();
@@ -6246,8 +6269,8 @@
     @NonNull
     public List<String> getDelegatePackages(ComponentName who, String scope)
             throws SecurityException {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        Preconditions.checkNotNull(scope, "Scope is null");
+        Objects.requireNonNull(who, "ComponentName is null");
+        Objects.requireNonNull(scope, "Scope is null");
         if (!Arrays.asList(DELEGATIONS).contains(scope)) {
             throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
         }
@@ -6329,7 +6352,7 @@
      * @return {@code true} if the calling process is a delegate of {@code scope}.
      */
     private boolean isCallerDelegate(String callerPackage, int callerUid, String scope) {
-        Preconditions.checkNotNull(callerPackage, "callerPackage is null");
+        Objects.requireNonNull(callerPackage, "callerPackage is null");
         if (!Arrays.asList(DELEGATIONS).contains(scope)) {
             throw new IllegalArgumentException("Unexpected delegation scope: " + scope);
         }
@@ -6410,7 +6433,7 @@
      */
     private void setDelegatedScopePreO(ComponentName who,
             String delegatePackage, String scope) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
@@ -6938,7 +6961,7 @@
             return null;
         }
         synchronized (getLockObject()) {
-            Preconditions.checkNotNull(who, "ComponentName is null");
+            Objects.requireNonNull(who, "ComponentName is null");
 
             // Only check if system user has set global proxy. We don't allow other users to set it.
             DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
@@ -7066,7 +7089,7 @@
         if (!mHasFeature) {
             return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             // Check for permissions
@@ -7220,7 +7243,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
@@ -7287,7 +7310,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -7347,7 +7370,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         // TODO (b/145286957) Refactor security checks
         enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
@@ -7369,7 +7392,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0;
@@ -7383,7 +7406,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         // TODO (b/145286957) Refactor security checks
         enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
@@ -7405,7 +7428,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwnerOrProfileOwnerOnUser0OrProfileOwnerOrganizationOwned();
 
         return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0;
@@ -7416,7 +7439,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         // Allow setting this policy to true only if there is a split system user.
         if (forceEphemeralUsers && !mInjector.userManagerIsSplitSystemUser()) {
             throw new UnsupportedOperationException(
@@ -7443,7 +7466,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             final ActiveAdmin deviceOwner =
                     getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -7472,7 +7495,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         // TODO: If an unaffiliated user is removed, the admin will be able to request a bugreport
         // which could still contain data related to that user. Should we disallow that, e.g. until
@@ -7709,7 +7732,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
@@ -7723,7 +7746,13 @@
             }
         }
         // Tell the user manager that the restrictions have changed.
-        pushUserRestrictions(parent ?  getProfileParentId(userHandle) : userHandle);
+        final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+        pushUserRestrictions(affectedUserId);
+
+        if (SecurityLog.isLoggingEnabled()) {
+            SecurityLog.writeEvent(SecurityLog.TAG_CAMERA_POLICY_SET,
+                    who.getPackageName(), userHandle, affectedUserId, disabled ? 1 : 0);
+        }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED)
                 .setAdmin(who)
@@ -7779,7 +7808,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         if (isManagedProfile(userHandle)) {
             if (parent) {
@@ -7867,7 +7896,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(packageList, "packageList is null");
+        Objects.requireNonNull(packageList, "packageList is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             // Ensure the caller is a DO or a keep uninstalled packages delegate.
@@ -8003,7 +8032,7 @@
         }
     }
 
-    private boolean canProfileOwnerAccessDeviceIds(int userId) {
+    private boolean isProfileOwnerOfOrganizationOwnedDevice(int userId) {
         synchronized (getLockObject()) {
             return mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId);
         }
@@ -8026,7 +8055,7 @@
     }
 
     private boolean isProfileOwnerOfOrganizationOwnedDevice(ComponentName who, int userId) {
-        return isProfileOwner(who, userId) && canProfileOwnerAccessDeviceIds(userId);
+        return isProfileOwner(who, userId) && isProfileOwnerOfOrganizationOwnedDevice(userId);
     }
 
     @Override
@@ -8104,7 +8133,7 @@
 
     @Override
     public void clearDeviceOwner(String packageName) {
-        Preconditions.checkNotNull(packageName, "packageName is null");
+        Objects.requireNonNull(packageName, "packageName is null");
         final int callingUid = mInjector.binderGetCallingUid();
         try {
             int uid = mInjector.getPackageManager().getPackageUidAsUser(packageName,
@@ -8259,7 +8288,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         final int userId = mInjector.userHandleGetCallingUserId();
         enforceNotManagedProfile(userId, "clear profile owner");
@@ -8300,7 +8329,7 @@
 
     @Override
     public void setDeviceOwnerLockScreenInfo(ComponentName who, CharSequence info) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         if (!mHasFeature) {
             return;
         }
@@ -8340,6 +8369,8 @@
         policy.mLockTaskPackages.clear();
         updateLockTaskPackagesLocked(policy.mLockTaskPackages, userId);
         policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+        policy.mProtectedPackages.clear();
+        updateProtectedPackagesLocked(policy.mProtectedPackages);
         saveSettingsLocked(userId);
 
         try {
@@ -8470,7 +8501,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             // Check if this is the profile owner who is calling
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -8497,7 +8528,7 @@
 
     @Override
     public void setProfileName(ComponentName who, String profileName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceProfileOrDeviceOwner(who);
 
         final int userId = UserHandle.getCallingUserId();
@@ -8522,7 +8553,6 @@
         if (!mHasFeature) {
             return null;
         }
-
         synchronized (getLockObject()) {
             return mOwners.getProfileOwnerComponent(userHandle);
         }
@@ -8553,7 +8583,7 @@
             for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
                 if (userInfo.isManagedProfile()) {
                     if (getProfileOwner(userInfo.id) != null
-                            && canProfileOwnerAccessDeviceIds(userInfo.id)) {
+                            && isProfileOwnerOfOrganizationOwnedDevice(userInfo.id)) {
                         ComponentName who = getProfileOwner(userInfo.id);
                         return getActiveAdminUncheckedLocked(who, userInfo.id);
                     }
@@ -8604,7 +8634,7 @@
         final boolean isCallerProfileOwnerOrDelegate = profileOwner != null
                 && (profileOwner.getPackageName().equals(packageName)
                         || isCallerDelegate(packageName, uid, DELEGATION_CERT_INSTALL));
-        if (isCallerProfileOwnerOrDelegate && canProfileOwnerAccessDeviceIds(userId)) {
+        if (isCallerProfileOwnerOrDelegate && isProfileOwnerOfOrganizationOwnedDevice(userId)) {
             return true;
         }
         //TODO(b/130844684): Temporarily allow profile owner on non-organization-owned devices
@@ -8817,6 +8847,26 @@
         }
     }
 
+    private void enforceAcrossUsersPermissions() {
+        if (isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID) {
+            return;
+        }
+        if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_PROFILES)
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS)
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS_FULL)
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        throw new SecurityException("Calling user does not have INTERACT_ACROSS_PROFILES or"
+                + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL permissions");
+    }
+
     private void enforceFullCrossUsersPermission(int userHandle) {
         enforceSystemUserOrPermissionIfCrossUser(userHandle,
                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
@@ -9037,6 +9087,10 @@
             pw.increaseIndent();
             pw.print("mPasswordOwner="); pw.println(policy.mPasswordOwner);
             pw.decreaseIndent();
+            pw.println();
+            pw.increaseIndent();
+            pw.print("mProtectedPackages="); pw.println(policy.mProtectedPackages);
+            pw.decreaseIndent();
         }
     }
 
@@ -9090,7 +9144,7 @@
     @Override
     public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter,
             ComponentName activity) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -9116,7 +9170,7 @@
 
     @Override
     public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -9135,7 +9189,7 @@
 
     @Override
     public void setDefaultSmsApplication(ComponentName admin, String packageName) {
-        Preconditions.checkNotNull(admin, "ComponentName is null");
+        Objects.requireNonNull(admin, "ComponentName is null");
         enforceDeviceOwner(admin);
         mInjector.binderWithCleanCallingIdentity(() ->
                 SmsApplication.setDefaultApplication(packageName, mContext));
@@ -9190,8 +9244,8 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return;
         }
-        Preconditions.checkNotNull(admin, "admin is null");
-        Preconditions.checkNotNull(agent, "agent is null");
+        Objects.requireNonNull(admin, "admin is null");
+        Objects.requireNonNull(agent, "agent is null");
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
@@ -9207,7 +9261,7 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return null;
         }
-        Preconditions.checkNotNull(agent, "agent null");
+        Objects.requireNonNull(agent, "agent null");
         enforceFullCrossUsersPermission(userHandle);
 
         synchronized (getLockObject()) {
@@ -9259,7 +9313,7 @@
 
     @Override
     public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
@@ -9281,7 +9335,7 @@
 
     @Override
     public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -9330,7 +9384,7 @@
 
     @Override
     public void clearCrossProfileIntentFilters(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         int callingUserId = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -9407,7 +9461,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         if (packageList != null) {
             int userId = UserHandle.getCallingUserId();
@@ -9460,7 +9514,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -9536,7 +9590,7 @@
         if (!mHasFeature) {
             return true;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(packageName, "packageName is null");
         enforceSystemCaller("query if an accessibility service is disabled by admin");
 
@@ -9587,7 +9641,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         if (packageList != null) {
             List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
@@ -9627,7 +9681,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -9681,7 +9735,7 @@
         if (!mHasFeature) {
             return true;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(packageName, "packageName is null");
         enforceSystemCaller("query if an input method is disabled by admin");
 
@@ -9704,7 +9758,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         if (!isManagedProfile(callingUserId)) {
@@ -9725,7 +9779,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(
@@ -9787,8 +9841,8 @@
     @Override
     public UserHandle createAndManageUser(ComponentName admin, String name,
             ComponentName profileOwner, PersistableBundle adminExtras, int flags) {
-        Preconditions.checkNotNull(admin, "admin is null");
-        Preconditions.checkNotNull(profileOwner, "profileOwner is null");
+        Objects.requireNonNull(admin, "admin is null");
+        Objects.requireNonNull(profileOwner, "profileOwner is null");
         if (!admin.getPackageName().equals(profileOwner.getPackageName())) {
             throw new IllegalArgumentException("profileOwner " + profileOwner + " and admin "
                     + admin + " are not in the same package");
@@ -9919,8 +9973,8 @@
 
     @Override
     public boolean removeUser(ComponentName who, UserHandle userHandle) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        Preconditions.checkNotNull(userHandle, "UserHandle is null");
+        Objects.requireNonNull(who, "ComponentName is null");
+        Objects.requireNonNull(userHandle, "UserHandle is null");
         enforceDeviceOwner(who);
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
@@ -9953,7 +10007,7 @@
 
     @Override
     public boolean switchUser(ComponentName who, UserHandle userHandle) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -9976,8 +10030,8 @@
 
     @Override
     public int startUserInBackground(ComponentName who, UserHandle userHandle) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        Preconditions.checkNotNull(userHandle, "UserHandle is null");
+        Objects.requireNonNull(who, "ComponentName is null");
+        Objects.requireNonNull(userHandle, "UserHandle is null");
         enforceDeviceOwner(who);
 
         final int userId = userHandle.getIdentifier();
@@ -10008,8 +10062,8 @@
 
     @Override
     public int stopUser(ComponentName who, UserHandle userHandle) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        Preconditions.checkNotNull(userHandle, "UserHandle is null");
+        Objects.requireNonNull(who, "ComponentName is null");
+        Objects.requireNonNull(userHandle, "UserHandle is null");
         enforceDeviceOwner(who);
 
         final int userId = userHandle.getIdentifier();
@@ -10023,7 +10077,7 @@
 
     @Override
     public int logoutUser(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
@@ -10077,7 +10131,7 @@
 
     @Override
     public List<UserHandle> getSecondaryUsers(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
 
         return mInjector.binderWithCleanCallingIdentity(() -> {
@@ -10096,7 +10150,7 @@
 
     @Override
     public boolean isEphemeralUser(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceProfileOrDeviceOwner(who);
 
         final int callingUserId = mInjector.userHandleGetCallingUserId();
@@ -10178,7 +10232,7 @@
     @Override
     public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner,
             boolean parent) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         if (!UserRestrictionsUtils.isValidRestriction(key)) {
             return;
         }
@@ -10324,7 +10378,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             final ActiveAdmin activeAdmin = getActiveAdminForCallerLocked(who,
@@ -10559,7 +10613,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10653,7 +10707,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10674,7 +10728,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10696,7 +10750,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10717,7 +10771,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10797,7 +10851,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10818,7 +10872,7 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
                     DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -10840,8 +10894,8 @@
     @Override
     public void setLockTaskPackages(ComponentName who, String[] packages)
             throws SecurityException {
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        Preconditions.checkNotNull(packages, "packages is null");
+        Objects.requireNonNull(who, "ComponentName is null");
+        Objects.requireNonNull(packages, "packages is null");
 
         synchronized (getLockObject()) {
             enforceCanCallLockTaskLocked(who);
@@ -10861,7 +10915,7 @@
 
     @Override
     public String[] getLockTaskPackages(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
         synchronized (getLockObject()) {
@@ -10881,7 +10935,7 @@
 
     @Override
     public void setLockTaskFeatures(ComponentName who, int flags) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         // Throw if Overview is used without Home.
         boolean hasHome = (flags & LOCK_TASK_FEATURE_HOME) != 0;
@@ -10908,7 +10962,7 @@
 
     @Override
     public int getLockTaskFeatures(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             enforceCanCallLockTaskLocked(who);
@@ -10978,7 +11032,7 @@
 
     @Override
     public void setGlobalSetting(ComponentName who, String setting, String value) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING)
@@ -11018,7 +11072,7 @@
 
     @Override
     public void setSystemSetting(ComponentName who, String setting, String value) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
 
         synchronized (getLockObject()) {
@@ -11038,7 +11092,7 @@
 
     @Override
     public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
 
         UserHandle userHandle = mInjector.binderGetCallingUserHandle();
@@ -11057,8 +11111,8 @@
 
     @Override
     public boolean setTime(ComponentName who, long millis) {
-        Preconditions.checkNotNull(who, "ComponentName is null in setTime");
-        enforceDeviceOwner(who);
+        Objects.requireNonNull(who, "ComponentName is null in setTime");
+        enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
         // Don't allow set time when auto time is on.
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) {
             return false;
@@ -11072,8 +11126,8 @@
 
     @Override
     public boolean setTimeZone(ComponentName who, String timeZone) {
-        Preconditions.checkNotNull(who, "ComponentName is null in setTimeZone");
-        enforceDeviceOwner(who);
+        Objects.requireNonNull(who, "ComponentName is null in setTimeZone");
+        enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(who);
         // Don't allow set timezone when auto timezone is on.
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
             return false;
@@ -11088,7 +11142,7 @@
 
     @Override
     public void setSecureSetting(ComponentName who, String setting, String value) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         int callingUserId = mInjector.userHandleGetCallingUserId();
 
         synchronized (getLockObject()) {
@@ -11159,7 +11213,7 @@
 
     @Override
     public void setMasterVolumeMuted(ComponentName who, boolean on) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false);
@@ -11173,7 +11227,7 @@
 
     @Override
     public boolean isMasterVolumeMuted(ComponentName who) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
@@ -11186,7 +11240,7 @@
     @Override
     public void setUserIcon(ComponentName who, Bitmap icon) {
         synchronized (getLockObject()) {
-            Preconditions.checkNotNull(who, "ComponentName is null");
+            Objects.requireNonNull(who, "ComponentName is null");
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             int userId = UserHandle.getCallingUserId();
@@ -11201,7 +11255,7 @@
 
     @Override
     public boolean setKeyguardDisabled(ComponentName who, boolean disabled) {
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userId = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -11664,7 +11718,7 @@
 
     @Override
     public Intent createAdminSupportIntent(String restriction) {
-        Preconditions.checkNotNull(restriction);
+        Objects.requireNonNull(restriction);
         final int uid = mInjector.binderGetCallingUid();
         final int userId = UserHandle.getUserId(uid);
         Intent intent = null;
@@ -11922,7 +11976,7 @@
 
     @Override
     public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin) {
-        Preconditions.checkNotNull(admin, "ComponentName is null");
+        Objects.requireNonNull(admin, "ComponentName is null");
         enforceProfileOrDeviceOwner(admin);
 
         return mOwners.getSystemUpdateInfo();
@@ -11964,7 +12018,7 @@
     public void setPermissionGrantState(ComponentName admin, String callerPackage,
             String packageName, String permission, int grantState, RemoteCallback callback)
             throws RemoteException {
-        Preconditions.checkNotNull(callback);
+        Objects.requireNonNull(callback);
 
         UserHandle user = mInjector.binderGetCallingUserHandle();
         synchronized (getLockObject()) {
@@ -12099,7 +12153,7 @@
 
     @Override
     public boolean isProvisioningAllowed(String action, String packageName) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
 
         final int callingUid = mInjector.binderGetCallingUid();
         final long ident = mInjector.binderClearCallingIdentity();
@@ -12119,7 +12173,7 @@
 
     @Override
     public int checkProvisioningPreCondition(String action, String packageName) {
-        Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
         enforceCanManageProfileAndDeviceOwners();
         return checkProvisioningPreConditionSkipPermission(action, packageName);
     }
@@ -12359,7 +12413,7 @@
 
     @Override
     public void reboot(ComponentName admin) {
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
         // Make sure caller has DO.
         enforceDeviceOwner(admin);
         mInjector.binderWithCleanCallingIdentity(() -> {
@@ -12380,7 +12434,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
@@ -12400,7 +12454,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             return admin.shortSupportMessage;
@@ -12412,7 +12466,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
@@ -12432,7 +12486,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForUidLocked(who, mInjector.binderGetCallingUid());
             return admin.longSupportMessage;
@@ -12444,7 +12498,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceSystemCaller("query support message for user");
 
         synchronized (getLockObject()) {
@@ -12461,7 +12515,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceSystemCaller("query support message for user");
 
         synchronized (getLockObject()) {
@@ -12478,7 +12532,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
         enforceManagedProfile(userHandle, "set organization color");
         synchronized (getLockObject()) {
@@ -12513,7 +12567,7 @@
         if (!mHasFeature) {
             return ActiveAdmin.DEF_ORGANIZATION_COLOR;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization color");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -12542,7 +12596,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         final int userHandle = mInjector.userHandleGetCallingUserId();
 
         synchronized (getLockObject()) {
@@ -12561,7 +12615,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceManagedProfile(mInjector.userHandleGetCallingUserId(), "get organization name");
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminForCallerLocked(who,
@@ -12599,8 +12653,8 @@
 
     @Override
     public List<String> setMeteredDataDisabledPackages(ComponentName who, List<String> packageNames) {
-        Preconditions.checkNotNull(who);
-        Preconditions.checkNotNull(packageNames);
+        Objects.requireNonNull(who);
+        Objects.requireNonNull(packageNames);
 
         if (!mHasFeature) {
             return packageNames;
@@ -12646,7 +12700,7 @@
 
     @Override
     public List<String> getMeteredDataDisabledPackages(ComponentName who) {
-        Preconditions.checkNotNull(who);
+        Objects.requireNonNull(who);
 
         if (!mHasFeature) {
             return new ArrayList<>();
@@ -12662,7 +12716,7 @@
     @Override
     public boolean isMeteredDataDisabledPackageForUser(ComponentName who,
             String packageName, int userId) {
-        Preconditions.checkNotNull(who);
+        Objects.requireNonNull(who);
 
         if (!mHasFeature) {
             return false;
@@ -12688,7 +12742,7 @@
     public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
         // As the caller is the system, it must specify the component name of the profile owner
         // as a sanity / safety check.
-        Preconditions.checkNotNull(who);
+        Objects.requireNonNull(who);
 
         if (!mHasFeature) {
             return;
@@ -12808,7 +12862,7 @@
             return Collections.emptyList();
         }
 
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             return new ArrayList<String>(
@@ -12877,7 +12931,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
@@ -12907,7 +12961,7 @@
 
         synchronized (getLockObject()) {
             if (!isCallerWithSystemUid()) {
-                Preconditions.checkNotNull(admin);
+                Objects.requireNonNull(admin);
                 getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
             }
             return mInjector.securityLogGetLoggingEnabledProperty();
@@ -12931,7 +12985,7 @@
             return null;
         }
 
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
         ensureDeviceOwnerAndAllUsersAffiliated(admin);
 
         DevicePolicyEventLogger
@@ -12961,7 +13015,7 @@
             return null;
         }
 
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
         ensureDeviceOwnerAndAllUsersAffiliated(admin);
 
         if (!mInjector.securityLogGetLoggingEnabledProperty()) {
@@ -13206,7 +13260,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
         enforceProfileOrDeviceOwner(admin);
         int userId = mInjector.userHandleGetCallingUserId();
         toggleBackupServiceActive(userId, enabled);
@@ -13214,7 +13268,7 @@
 
     @Override
     public boolean isBackupServiceEnabled(ComponentName admin) {
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
         if (!mHasFeature) {
             return true;
         }
@@ -13239,14 +13293,14 @@
         if (!mHasFeature) {
             return false;
         }
-        Preconditions.checkNotNull(admin);
-        Preconditions.checkNotNull(caller);
-        Preconditions.checkNotNull(serviceIntent);
+        Objects.requireNonNull(admin);
+        Objects.requireNonNull(caller);
+        Objects.requireNonNull(serviceIntent);
         Preconditions.checkArgument(
                 serviceIntent.getComponent() != null || serviceIntent.getPackage() != null,
                 "Service intent must be explicit (with a package name or component): "
                         + serviceIntent);
-        Preconditions.checkNotNull(connection);
+        Objects.requireNonNull(connection);
         Preconditions.checkArgument(mInjector.userHandleGetCallingUserId() != targetUserId,
                 "target user id must be different from the calling user id");
 
@@ -13290,7 +13344,7 @@
         if (!mHasFeature) {
             return Collections.emptyList();
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -13755,7 +13809,7 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return false;
         }
-        Preconditions.checkNotNull(token);
+        Objects.requireNonNull(token);
         synchronized (getLockObject()) {
             final int userHandle = mInjector.userHandleGetCallingUserId();
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -13791,9 +13845,9 @@
     @Override
     public void clearApplicationUserData(ComponentName admin, String packageName,
             IPackageDataObserver callback) {
-        Preconditions.checkNotNull(admin, "ComponentName is null");
-        Preconditions.checkNotNull(packageName, "packageName is null");
-        Preconditions.checkNotNull(callback, "callback is null");
+        Objects.requireNonNull(admin, "ComponentName is null");
+        Objects.requireNonNull(packageName, "packageName is null");
+        Objects.requireNonNull(callback, "callback is null");
         enforceProfileOrDeviceOwner(admin);
         final int userId = UserHandle.getCallingUserId();
 
@@ -13824,7 +13878,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         synchronized (getLockObject()) {
             ActiveAdmin deviceOwner =
@@ -13865,8 +13919,8 @@
             return;
         }
 
-        Preconditions.checkNotNull(admin, "Admin cannot be null.");
-        Preconditions.checkNotNull(target, "Target cannot be null.");
+        Objects.requireNonNull(admin, "Admin cannot be null.");
+        Objects.requireNonNull(target, "Target cannot be null.");
 
         enforceProfileOrDeviceOwner(admin);
 
@@ -14001,7 +14055,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         final String startUserSessionMessageString =
                 startUserSessionMessage != null ? startUserSessionMessage.toString() : null;
@@ -14026,7 +14080,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         final String endUserSessionMessageString =
                 endUserSessionMessage != null ? endUserSessionMessage.toString() : null;
@@ -14051,7 +14105,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         synchronized (getLockObject()) {
             final ActiveAdmin deviceOwner =
@@ -14065,7 +14119,7 @@
         if (!mHasFeature) {
             return null;
         }
-        Preconditions.checkNotNull(admin);
+        Objects.requireNonNull(admin);
 
         synchronized (getLockObject()) {
             final ActiveAdmin deviceOwner =
@@ -14108,22 +14162,18 @@
         if (!mHasFeature || !mHasTelephonyFeature) {
             return -1;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null in addOverrideApn");
-        Preconditions.checkNotNull(apnSetting, "ApnSetting is null in addOverrideApn");
+        Objects.requireNonNull(who, "ComponentName is null in addOverrideApn");
+        Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn");
         enforceDeviceOwner(who);
 
-        int operatedId = -1;
-        Uri resultUri = mInjector.binderWithCleanCallingIdentity(() ->
-                mContext.getContentResolver().insert(DPC_URI, apnSetting.toContentValues()));
-        if (resultUri != null) {
-            try {
-                operatedId = Integer.parseInt(resultUri.getLastPathSegment());
-            } catch (NumberFormatException e) {
-                Slog.e(LOG_TAG, "Failed to parse inserted override APN id.", e);
-            }
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        if (tm != null) {
+            return mInjector.binderWithCleanCallingIdentity(
+                    () -> tm.addDevicePolicyOverrideApn(mContext, apnSetting));
+        } else {
+            Log.w(LOG_TAG, "TelephonyManager is null when trying to add override apn");
+            return Telephony.Carriers.INVALID_APN_ID;
         }
-
-        return operatedId;
     }
 
     @Override
@@ -14132,17 +14182,21 @@
         if (!mHasFeature || !mHasTelephonyFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null in updateOverrideApn");
-        Preconditions.checkNotNull(apnSetting, "ApnSetting is null in updateOverrideApn");
+        Objects.requireNonNull(who, "ComponentName is null in updateOverrideApn");
+        Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn");
         enforceDeviceOwner(who);
 
         if (apnId < 0) {
             return false;
         }
-        return mInjector.binderWithCleanCallingIdentity(() ->
-                mContext.getContentResolver().update(
-                        Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)),
-                        apnSetting.toContentValues(), null, null) > 0);
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        if (tm != null) {
+            return mInjector.binderWithCleanCallingIdentity(
+                    () -> tm.modifyDevicePolicyOverrideApn(mContext, apnId, apnSetting));
+        } else {
+            Log.w(LOG_TAG, "TelephonyManager is null when trying to modify override apn");
+            return false;
+        }
     }
 
     @Override
@@ -14150,7 +14204,7 @@
         if (!mHasFeature || !mHasTelephonyFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null in removeOverrideApn");
+        Objects.requireNonNull(who, "ComponentName is null in removeOverrideApn");
         enforceDeviceOwner(who);
 
         return removeOverrideApnUnchecked(apnId);
@@ -14171,30 +14225,20 @@
         if (!mHasFeature || !mHasTelephonyFeature) {
             return Collections.emptyList();
         }
-        Preconditions.checkNotNull(who, "ComponentName is null in getOverrideApns");
+        Objects.requireNonNull(who, "ComponentName is null in getOverrideApns");
         enforceDeviceOwner(who);
 
         return getOverrideApnsUnchecked();
     }
 
     private List<ApnSetting> getOverrideApnsUnchecked() {
-        final Cursor cursor = mInjector.binderWithCleanCallingIdentity(
-                () -> mContext.getContentResolver().query(DPC_URI, null, null, null, null));
-
-        if (cursor == null) {
-            return Collections.emptyList();
+        TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        if (tm != null) {
+            return mInjector.binderWithCleanCallingIdentity(
+                    () -> tm.getDevicePolicyOverrideApns(mContext));
         }
-        try {
-            List<ApnSetting> apnList = new ArrayList<ApnSetting>();
-            cursor.moveToPosition(-1);
-            while (cursor.moveToNext()) {
-                ApnSetting apn = ApnSetting.makeApnSetting(cursor);
-                apnList.add(apn);
-            }
-            return apnList;
-        } finally {
-            cursor.close();
-        }
+        Log.w(LOG_TAG, "TelephonyManager is null when trying to get override apns");
+        return Collections.emptyList();
     }
 
     @Override
@@ -14202,7 +14246,7 @@
         if (!mHasFeature || !mHasTelephonyFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null in setOverrideApnEnabled");
+        Objects.requireNonNull(who, "ComponentName is null in setOverrideApnEnabled");
         enforceDeviceOwner(who);
 
         setOverrideApnsEnabledUnchecked(enabled);
@@ -14220,7 +14264,7 @@
         if (!mHasFeature || !mHasTelephonyFeature) {
             return false;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null in isOverrideApnEnabled");
+        Objects.requireNonNull(who, "ComponentName is null in isOverrideApnEnabled");
         enforceDeviceOwner(who);
 
         Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity(
@@ -14304,7 +14348,7 @@
             return PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
         }
 
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
 
         final int returnCode;
@@ -14342,7 +14386,7 @@
             return PRIVATE_DNS_MODE_UNKNOWN;
         }
 
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
         String currentMode = mInjector.settingsGlobalGetString(PRIVATE_DNS_MODE);
         if (currentMode == null) {
@@ -14366,7 +14410,7 @@
             return null;
         }
 
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
         enforceDeviceOwner(who);
 
         return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER);
@@ -14404,7 +14448,7 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminForCallerLocked(
@@ -14425,7 +14469,7 @@
         if (!mHasFeature) {
             return Collections.emptyList();
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminForCallerLocked(
@@ -14479,8 +14523,8 @@
         if (!mHasFeature) {
             return;
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
-        Preconditions.checkNotNull(packageNames, "Package names is null");
+        Objects.requireNonNull(who, "ComponentName is null");
+        Objects.requireNonNull(packageNames, "Package names is null");
         synchronized (getLockObject()) {
             final ActiveAdmin admin =
                     getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
@@ -14494,7 +14538,7 @@
         if (!mHasFeature) {
             return Collections.emptyList();
         }
-        Preconditions.checkNotNull(who, "ComponentName is null");
+        Objects.requireNonNull(who, "ComponentName is null");
 
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getActiveAdminForCallerLocked(
@@ -14504,6 +14548,51 @@
     }
 
     @Override
+    public List<String> getAllCrossProfilePackages() {
+        if (!mHasFeature) {
+            return Collections.emptyList();
+        }
+        enforceAcrossUsersPermissions();
+
+        synchronized (getLockObject()) {
+            final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup();
+            final List<String> packages = getCrossProfilePackagesForAdmins(admins);
+
+            packages.addAll(getDefaultCrossProfilePackages());
+
+            return packages;
+        }
+    }
+
+    private List<String> getCrossProfilePackagesForAdmins(List<ActiveAdmin> admins) {
+        final List<String> packages = new ArrayList<>();
+        for (int i = 0; i < admins.size(); i++) {
+            packages.addAll(admins.get(i).mCrossProfilePackages);
+        }
+        return packages;
+    }
+
+    private List<String> getDefaultCrossProfilePackages() {
+        return Arrays.asList(mContext.getResources()
+                .getStringArray(R.array.cross_profile_apps));
+    }
+
+    private List<ActiveAdmin> getProfileOwnerAdminsForCurrentProfileGroup() {
+        synchronized (getLockObject()) {
+            final List<ActiveAdmin> admins = new ArrayList<>();
+            int[] users = mUserManager.getProfileIdsWithDisabled(UserHandle.getCallingUserId());
+            for (int i = 0; i < users.length; i++) {
+                final ComponentName componentName = getProfileOwner(users[i]);
+                if (componentName != null) {
+                    admins.add(getActiveAdminForCallerLocked(
+                            componentName, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER));
+                }
+            }
+            return admins;
+        }
+    }
+
+    @Override
     public boolean isManagedKiosk() {
         if (!mHasFeature) {
             return false;
@@ -14635,4 +14724,42 @@
         return DevicePolicyConstants.loadFromString(
                 mInjector.settingsGlobalGetString(Global.DEVICE_POLICY_CONSTANTS));
     }
+
+    @Override
+    public void setProtectedPackages(ComponentName who, List<String> packages) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+        Preconditions.checkNotNull(packages, "packages is null");
+
+        enforceDeviceOwner(who);
+        synchronized (getLockObject()) {
+            final int userHandle = mInjector.userHandleGetCallingUserId();
+            setProtectedPackagesLocked(userHandle, packages);
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.SET_PACKAGES_PROTECTED)
+                    .setAdmin(who)
+                    .setStrings(packages.toArray(new String[packages.size()]))
+                    .write();
+        }
+    }
+
+    private void setProtectedPackagesLocked(int userHandle, List<String> packages) {
+        final DevicePolicyData policy = getUserData(userHandle);
+        policy.mProtectedPackages = packages;
+
+        // Store the settings persistently.
+        saveSettingsLocked(userHandle);
+        updateProtectedPackagesLocked(packages);
+    }
+
+    @Override
+    public List<String> getProtectedPackages(ComponentName who) {
+        Preconditions.checkNotNull(who, "ComponentName is null");
+
+        enforceDeviceOwner(who);
+        final int userHandle = mInjector.binderGetCallingUserHandle().getIdentifier();
+        synchronized (getLockObject()) {
+            final List<String> packages = getUserData(userHandle).mProtectedPackages;
+            return packages == null ? Collections.EMPTY_LIST : packages;
+        }
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
index dd3bfc3..4b66bea 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/TransferOwnershipMetadataManager.java
@@ -39,6 +39,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.Objects;
 
 /**
  * Handles reading and writing of the owner transfer metadata file.
@@ -185,8 +186,8 @@
                 @NonNull int userId, @NonNull String adminType) {
             this.sourceComponent = sourceComponent;
             this.targetComponent = targetComponent;
-            Preconditions.checkNotNull(sourceComponent);
-            Preconditions.checkNotNull(targetComponent);
+            Objects.requireNonNull(sourceComponent);
+            Objects.requireNonNull(targetComponent);
             Preconditions.checkStringNotEmpty(adminType);
             this.userId = userId;
             this.adminType = adminType;
@@ -199,7 +200,7 @@
         }
 
         private static ComponentName unflattenComponentUnchecked(String flatComponent) {
-            Preconditions.checkNotNull(flatComponent);
+            Objects.requireNonNull(flatComponent);
             return ComponentName.unflattenFromString(flatComponent);
         }
 
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 2661925..ddf4dd5 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -69,7 +69,6 @@
     name: "service.incremental",
     defaults: [
         "service.incremental-defaults",
-        "linux_bionic_supported",
     ],
 
     export_include_dirs: ["include/",],
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cfe1318..bfec51c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -126,6 +126,7 @@
 import com.android.server.os.BugreportManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
 import com.android.server.os.SchedulingPolicyService;
+import com.android.server.people.PeopleService;
 import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.pm.CrossProfileAppsService;
 import com.android.server.pm.DataLoaderManagerService;
@@ -214,6 +215,8 @@
             "com.android.server.companion.CompanionDeviceManagerService";
     private static final String STATS_COMPANION_LIFECYCLE_CLASS =
             "com.android.server.stats.StatsCompanion$Lifecycle";
+    private static final String STATS_PULL_ATOM_SERVICE_CLASS =
+            "com.android.server.stats.StatsPullAtomService";
     private static final String USB_SERVICE_CLASS =
             "com.android.server.usb.UsbService$Lifecycle";
     private static final String MIDI_SERVICE_CLASS =
@@ -1915,6 +1918,10 @@
             t.traceBegin("StartCrossProfileAppsService");
             mSystemServiceManager.startService(CrossProfileAppsService.class);
             t.traceEnd();
+
+            t.traceBegin("StartPeopleService");
+            mSystemServiceManager.startService(PeopleService.class);
+            t.traceEnd();
         }
 
         if (!isWatch) {
@@ -1975,6 +1982,11 @@
         mSystemServiceManager.startService(STATS_COMPANION_LIFECYCLE_CLASS);
         t.traceEnd();
 
+        // Statsd pulled atoms
+        t.traceBegin("StartStatsPullAtomService");
+        mSystemServiceManager.startService(STATS_PULL_ATOM_SERVICE_CLASS);
+        t.traceEnd();
+
         // Incidentd and dumpstated helper
         t.traceBegin("StartIncidentCompanionService");
         mSystemServiceManager.startService(IncidentCompanionService.class);
diff --git a/services/people/Android.bp b/services/people/Android.bp
new file mode 100644
index 0000000..d64097a
--- /dev/null
+++ b/services/people/Android.bp
@@ -0,0 +1,5 @@
+java_library_static {
+    name: "services.people",
+    srcs: ["java/**/*.java"],
+    libs: ["services.core"],
+}
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
new file mode 100644
index 0000000..ef3da60
--- /dev/null
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.IPredictionCallback;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+/**
+ * A service that manages the people and conversations provided by apps.
+ */
+public class PeopleService extends SystemService {
+
+    private static final String TAG = "PeopleService";
+
+    /**
+     * Initializes the system service.
+     *
+     * @param context The system server context.
+     */
+    public PeopleService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        publishLocalService(PeopleServiceInternal.class, new LocalService());
+    }
+
+    @VisibleForTesting
+    final class LocalService extends PeopleServiceInternal {
+
+        private Map<AppPredictionSessionId, SessionInfo> mSessions = new ArrayMap<>();
+
+        @Override
+        public void onCreatePredictionSession(AppPredictionContext context,
+                AppPredictionSessionId sessionId) {
+            mSessions.put(sessionId, new SessionInfo(context));
+        }
+
+        @Override
+        public void notifyAppTargetEvent(AppPredictionSessionId sessionId, AppTargetEvent event) {
+            runForSession(sessionId,
+                    sessionInfo -> sessionInfo.getPredictor().onAppTargetEvent(event));
+        }
+
+        @Override
+        public void notifyLaunchLocationShown(AppPredictionSessionId sessionId,
+                String launchLocation, ParceledListSlice targetIds) {
+            runForSession(sessionId,
+                    sessionInfo -> sessionInfo.getPredictor().onLaunchLocationShown(
+                            launchLocation, targetIds.getList()));
+        }
+
+        @Override
+        public void sortAppTargets(AppPredictionSessionId sessionId, ParceledListSlice targets,
+                IPredictionCallback callback) {
+            runForSession(sessionId,
+                    sessionInfo -> sessionInfo.getPredictor().onSortAppTargets(
+                            targets.getList(),
+                            targetList -> invokePredictionCallback(callback, targetList)));
+        }
+
+        @Override
+        public void registerPredictionUpdates(AppPredictionSessionId sessionId,
+                IPredictionCallback callback) {
+            runForSession(sessionId, sessionInfo -> sessionInfo.addCallback(callback));
+        }
+
+        @Override
+        public void unregisterPredictionUpdates(AppPredictionSessionId sessionId,
+                IPredictionCallback callback) {
+            runForSession(sessionId, sessionInfo -> sessionInfo.removeCallback(callback));
+        }
+
+        @Override
+        public void requestPredictionUpdate(AppPredictionSessionId sessionId) {
+            runForSession(sessionId,
+                    sessionInfo -> sessionInfo.getPredictor().onRequestPredictionUpdate());
+        }
+
+        @Override
+        public void onDestroyPredictionSession(AppPredictionSessionId sessionId) {
+            runForSession(sessionId, sessionInfo -> {
+                sessionInfo.onDestroy();
+                mSessions.remove(sessionId);
+            });
+        }
+
+        @VisibleForTesting
+        SessionInfo getSessionInfo(AppPredictionSessionId sessionId) {
+            return mSessions.get(sessionId);
+        }
+
+        private void runForSession(AppPredictionSessionId sessionId, Consumer<SessionInfo> method) {
+            SessionInfo sessionInfo = mSessions.get(sessionId);
+            if (sessionInfo == null) {
+                Slog.e(TAG, "Failed to find the session: " + sessionId);
+                return;
+            }
+            method.accept(sessionInfo);
+        }
+
+        private void invokePredictionCallback(IPredictionCallback callback,
+                List<AppTarget> targets) {
+            try {
+                callback.onResult(new ParceledListSlice<>(targets));
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to calling callback" + e);
+            }
+        }
+    }
+}
diff --git a/services/people/java/com/android/server/people/SessionInfo.java b/services/people/java/com/android/server/people/SessionInfo.java
new file mode 100644
index 0000000..df7cedf
--- /dev/null
+++ b/services/people/java/com/android/server/people/SessionInfo.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.IPredictionCallback;
+import android.content.pm.ParceledListSlice;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.people.prediction.ConversationPredictor;
+
+import java.util.List;
+
+/** Manages the information and callbacks in an app prediction request session. */
+class SessionInfo {
+
+    private static final String TAG = "SessionInfo";
+
+    private final ConversationPredictor mConversationPredictor;
+    private final RemoteCallbackList<IPredictionCallback> mCallbacks =
+            new RemoteCallbackList<>();
+
+    SessionInfo(AppPredictionContext predictionContext) {
+        mConversationPredictor = new ConversationPredictor(predictionContext,
+                this::updatePredictions);
+    }
+
+    void addCallback(IPredictionCallback callback) {
+        mCallbacks.register(callback);
+    }
+
+    void removeCallback(IPredictionCallback callback) {
+        mCallbacks.unregister(callback);
+    }
+
+    ConversationPredictor getPredictor() {
+        return mConversationPredictor;
+    }
+
+    void onDestroy() {
+        mCallbacks.kill();
+    }
+
+    private void updatePredictions(List<AppTarget> targets) {
+        int callbackCount = mCallbacks.beginBroadcast();
+        for (int i = 0; i < callbackCount; i++) {
+            try {
+                mCallbacks.getBroadcastItem(i).onResult(new ParceledListSlice<>(targets));
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to calling callback" + e);
+            }
+        }
+        mCallbacks.finishBroadcast();
+    }
+}
diff --git a/services/people/java/com/android/server/people/prediction/ConversationPredictor.java b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
new file mode 100644
index 0000000..de71d29
--- /dev/null
+++ b/services/people/java/com/android/server/people/prediction/ConversationPredictor.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people.prediction;
+
+import android.annotation.MainThread;
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.function.Consumer;
+
+/**
+ * Predictor that predicts the conversations or apps the user is most likely to open.
+ */
+public class ConversationPredictor {
+
+    private final AppPredictionContext mPredictionContext;
+    private final Consumer<List<AppTarget>> mUpdatePredictionsMethod;
+    private final ExecutorService mCallbackExecutor;
+
+    public ConversationPredictor(AppPredictionContext predictionContext,
+            Consumer<List<AppTarget>> updatePredictionsMethod) {
+        mPredictionContext = predictionContext;
+        mUpdatePredictionsMethod = updatePredictionsMethod;
+        mCallbackExecutor = Executors.newSingleThreadExecutor();
+    }
+
+    /**
+     * Called by the client app to indicate a target launch.
+     */
+    @MainThread
+    public void onAppTargetEvent(AppTargetEvent event) {
+    }
+
+    /**
+     * Called by the client app to indicate a particular location has been shown to the user.
+     */
+    @MainThread
+    public void onLaunchLocationShown(String launchLocation, List<AppTargetId> targetIds) {
+    }
+
+    /**
+     * Called by the client app to request sorting of the provided targets based on the prediction
+     * ranking.
+     */
+    @MainThread
+    public void onSortAppTargets(List<AppTarget> targets, Consumer<List<AppTarget>> callback) {
+        mCallbackExecutor.execute(() -> callback.accept(targets));
+    }
+
+    /**
+     * Called by the client app to request target predictions.
+     */
+    @MainThread
+    public void onRequestPredictionUpdate() {
+        List<AppTarget> targets = new ArrayList<>();
+        mCallbackExecutor.execute(() -> mUpdatePredictionsMethod.accept(targets));
+    }
+
+    @VisibleForTesting
+    public Consumer<List<AppTarget>> getUpdatePredictionsMethod() {
+        return mUpdatePredictionsMethod;
+    }
+}
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index c9b9f3e..d064f7e 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -78,6 +78,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * SystemService wrapper for the PrintManager implementation. Publishes
@@ -137,7 +138,7 @@
         @Override
         public Bundle print(String printJobName, IPrintDocumentAdapter adapter,
                 PrintAttributes attributes, String packageName, int appId, int userId) {
-            adapter = Preconditions.checkNotNull(adapter);
+            Objects.requireNonNull(adapter);
             if (!isPrintingEnabled()) {
                 CharSequence disabledMessage = null;
                 DevicePolicyManagerInternal dpmi =
@@ -239,7 +240,7 @@
 
         @Override
         public Icon getCustomPrinterIcon(PrinterId printerId, int userId) {
-            printerId = Preconditions.checkNotNull(printerId);
+            Objects.requireNonNull(printerId);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -349,7 +350,7 @@
                 return;
             }
 
-            service = Preconditions.checkNotNull(service);
+            Objects.requireNonNull(service);
 
             final UserState userState;
             synchronized (mLock) {
@@ -391,7 +392,7 @@
         @Override
         public void createPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
                 int userId) {
-            observer = Preconditions.checkNotNull(observer);
+            Objects.requireNonNull(observer);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -413,7 +414,7 @@
         @Override
         public void destroyPrinterDiscoverySession(IPrinterDiscoveryObserver observer,
                 int userId) {
-            observer = Preconditions.checkNotNull(observer);
+            Objects.requireNonNull(observer);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -435,7 +436,7 @@
         @Override
         public void startPrinterDiscovery(IPrinterDiscoveryObserver observer,
                 List<PrinterId> priorityList, int userId) {
-            observer = Preconditions.checkNotNull(observer);
+            Objects.requireNonNull(observer);
             if (priorityList != null) {
                 priorityList = Preconditions.checkCollectionElementsNotNull(priorityList,
                         "PrinterId");
@@ -460,7 +461,7 @@
 
         @Override
         public void stopPrinterDiscovery(IPrinterDiscoveryObserver observer, int userId) {
-            observer = Preconditions.checkNotNull(observer);
+            Objects.requireNonNull(observer);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -502,7 +503,7 @@
 
         @Override
         public void startPrinterStateTracking(PrinterId printerId, int userId) {
-            printerId = Preconditions.checkNotNull(printerId);
+            Objects.requireNonNull(printerId);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -523,7 +524,7 @@
 
         @Override
         public void stopPrinterStateTracking(PrinterId printerId, int userId) {
-            printerId = Preconditions.checkNotNull(printerId);
+            Objects.requireNonNull(printerId);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -545,7 +546,7 @@
         @Override
         public void addPrintJobStateChangeListener(IPrintJobStateChangeListener listener,
                 int appId, int userId) throws RemoteException {
-            listener = Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final int resolvedAppId;
@@ -569,7 +570,7 @@
         @Override
         public void removePrintJobStateChangeListener(IPrintJobStateChangeListener listener,
                 int userId) {
-            listener = Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
 
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
@@ -591,7 +592,7 @@
         @Override
         public void addPrintServicesChangeListener(IPrintServicesChangeListener listener,
                 int userId) throws RemoteException {
-            listener = Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
 
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
                     null);
@@ -615,7 +616,7 @@
         @Override
         public void removePrintServicesChangeListener(IPrintServicesChangeListener listener,
                 int userId) {
-            listener = Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
 
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRINT_SERVICES,
                     null);
@@ -640,7 +641,7 @@
         public void addPrintServiceRecommendationsChangeListener(
                 IRecommendationsChangeListener listener, int userId)
                 throws RemoteException {
-            listener = Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
 
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
@@ -664,7 +665,7 @@
         @Override
         public void removePrintServiceRecommendationsChangeListener(
                 IRecommendationsChangeListener listener, int userId) {
-            listener = Preconditions.checkNotNull(listener);
+            Objects.requireNonNull(listener);
 
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRINT_SERVICE_RECOMMENDATIONS, null);
@@ -688,7 +689,7 @@
 
         @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-            fd = Preconditions.checkNotNull(fd);
+            Objects.requireNonNull(fd);
 
             if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 529339e..067f23a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -16,6 +16,8 @@
 package com.android.server.appop;
 
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
+import static android.app.AppOpsManager.FILTER_BY_UID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_ERRORED;
 import static android.app.AppOpsManager.MODE_FOREGROUND;
@@ -48,6 +50,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.parsing.AndroidPackage;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
@@ -68,6 +71,7 @@
 import org.mockito.quality.Strictness;
 
 import java.io.File;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -138,11 +142,15 @@
                 .spyStatic(Settings.Global.class)
                 .startMocking();
 
-        // Mock LocalServices.getService(PackageManagerInternal.class).getApplicationInfo dependency
+        // Mock LocalServices.getService(PackageManagerInternal.class).getPackage dependency
         // needed by AppOpsService
         PackageManagerInternal mockPackageManagerInternal = mock(PackageManagerInternal.class);
-        when(mockPackageManagerInternal.getApplicationInfo(eq(sMyPackageName), anyInt(), anyInt(),
-                anyInt())).thenReturn(sContext.getApplicationInfo());
+        AndroidPackage mockMyPkg = mock(AndroidPackage.class);
+        when(mockMyPkg.isPrivileged()).thenReturn(false);
+        when(mockMyPkg.getUid()).thenReturn(mMyUid);
+        when(mockMyPkg.getFeatures()).thenReturn(Collections.emptyList());
+
+        when(mockPackageManagerInternal.getPackage(sMyPackageName)).thenReturn(mockMyPkg);
         doReturn(mockPackageManagerInternal).when(
                 () -> LocalServices.getService(PackageManagerInternal.class));
 
@@ -162,12 +170,12 @@
         mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
 
         // Note an op that's allowed.
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
         List<PackageOps> loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
 
         // Note another op that's not allowed.
-        mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null);
         loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
         assertContainsOp(loggedOps, OP_WRITE_SMS, -1, mTestStartMillis, MODE_ERRORED);
@@ -183,16 +191,16 @@
         // This op controls WIFI_SCAN
         mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ALLOWED);
 
-        assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
+                null)).isEqualTo(MODE_ALLOWED);
 
         assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, -1,
                 MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
 
         // Now set COARSE_LOCATION to ERRORED -> this will make WIFI_SCAN disabled as well.
         mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_ERRORED);
-        assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ERRORED);
+        assertThat(mAppOpsService.noteOperation(OP_WIFI_SCAN, mMyUid, sMyPackageName, null, false,
+                null)).isEqualTo(MODE_ERRORED);
 
         assertContainsOp(getLoggedOps(), OP_WIFI_SCAN, mTestStartMillis, mTestStartMillis,
                 MODE_ALLOWED /* default for WIFI_SCAN; this is not changed or used in this test */);
@@ -203,8 +211,8 @@
     public void testStatePersistence() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
         mAppOpsService.setMode(OP_WRITE_SMS, mMyUid, sMyPackageName, MODE_ERRORED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
-        mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
+        mAppOpsService.noteOperation(OP_WRITE_SMS, mMyUid, sMyPackageName, null, false, null);
         mAppOpsService.writeState();
 
         // Create a new app ops service, and initialize its state from XML.
@@ -221,7 +229,7 @@
     @Test
     public void testShutdown() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
         mAppOpsService.shutdown();
 
         // Create a new app ops service, and initialize its state from XML.
@@ -236,7 +244,7 @@
     @Test
     public void testGetOpsForPackage() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
 
         // Query all ops
         List<PackageOps> loggedOps = mAppOpsService.getOpsForPackage(
@@ -265,7 +273,7 @@
     @Test
     public void testPackageRemoved() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
 
         List<PackageOps> loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -278,10 +286,10 @@
     @Test
     public void testPackageRemovedHistoricalOps() throws InterruptedException {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
 
         AppOpsManager.HistoricalOps historicalOps = new AppOpsManager.HistoricalOps(0, 15000);
-        historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName,
+        historicalOps.increaseAccessCount(OP_READ_SMS, mMyUid, sMyPackageName, null,
                 AppOpsManager.UID_STATE_PERSISTENT, 0, 1);
 
         mAppOpsService.addHistoricalOps(historicalOps);
@@ -294,8 +302,8 @@
         });
 
         // First, do a fetch to ensure it's written
-        mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, 0, Long.MAX_VALUE, 0,
-                callback);
+        mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, null,
+                FILTER_BY_UID | FILTER_BY_PACKAGE_NAME, 0, Long.MAX_VALUE, 0, callback);
 
         latchRef.get().await(5, TimeUnit.SECONDS);
         assertThat(latchRef.get().getCount()).isEqualTo(0);
@@ -306,8 +314,8 @@
 
         latchRef.set(new CountDownLatch(1));
 
-        mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, 0, Long.MAX_VALUE, 0,
-                callback);
+        mAppOpsService.getHistoricalOps(mMyUid, sMyPackageName, null, null,
+                FILTER_BY_UID | FILTER_BY_PACKAGE_NAME, 0, Long.MAX_VALUE, 0, callback);
 
         latchRef.get().await(5, TimeUnit.SECONDS);
         assertThat(latchRef.get().getCount()).isEqualTo(0);
@@ -317,7 +325,7 @@
     @Test
     public void testUidRemoved() {
         mAppOpsService.setMode(OP_READ_SMS, mMyUid, sMyPackageName, MODE_ALLOWED);
-        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null);
+        mAppOpsService.noteOperation(OP_READ_SMS, mMyUid, sMyPackageName, null, false, null);
 
         List<PackageOps> loggedOps = getLoggedOps();
         assertContainsOp(loggedOps, OP_READ_SMS, mTestStartMillis, -1, MODE_ALLOWED);
@@ -340,13 +348,13 @@
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP,
                 ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -354,8 +362,8 @@
         Thread.sleep(50);
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
     }
 
     @Test
@@ -363,13 +371,13 @@
         setupProcStateTests();
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
     }
 
     @Test
@@ -378,13 +386,13 @@
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isEqualTo(MODE_ALLOWED);
     }
 
     @Test
@@ -393,13 +401,13 @@
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP,
                 ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -407,8 +415,8 @@
         Thread.sleep(50);
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
     }
 
     @Test
@@ -417,13 +425,13 @@
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP,
                 ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
@@ -431,8 +439,8 @@
         Thread.sleep(50);
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isEqualTo(MODE_ALLOWED);
 
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
@@ -440,8 +448,8 @@
         Thread.sleep(50);
         mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
                 ActivityManager.PROCESS_CAPABILITY_NONE);
-        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null))
-                .isNotEqualTo(MODE_ALLOWED);
+        assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
+                false, null)).isNotEqualTo(MODE_ALLOWED);
     }
 
     private List<PackageOps> getLoggedOps() {
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
new file mode 100644
index 0000000..80aec73
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/CountQuotaTrackerTest.java
@@ -0,0 +1,848 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.utils.quota;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.utils.quota.Category.SINGLE_CATEGORY;
+import static com.android.server.utils.quota.QuotaTracker.MAX_WINDOW_SIZE_MS;
+import static com.android.server.utils.quota.QuotaTracker.MIN_WINDOW_SIZE_MS;
+
+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.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.LongArrayQueue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.utils.quota.CountQuotaTracker.ExecutionStats;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link CountQuotaTracker}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class CountQuotaTrackerTest {
+    private static final long SECOND_IN_MILLIS = 1000L;
+    private static final long MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
+    private static final long HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
+    private static final String TAG_CLEANUP = "*CountQuotaTracker.cleanup*";
+    private static final String TAG_QUOTA_CHECK = "*QuotaTracker.quota_check*";
+    private static final String TEST_PACKAGE = "com.android.frameworks.mockingservicestests";
+    private static final String TEST_TAG = "testing";
+    private static final int TEST_UID = 10987;
+    private static final int TEST_USER_ID = 0;
+
+    /** A {@link Category} to represent the ACTIVE standby bucket. */
+    private static final Category ACTIVE_BUCKET_CATEGORY = new Category("ACTIVE");
+
+    /** A {@link Category} to represent the WORKING_SET standby bucket. */
+    private static final Category WORKING_SET_BUCKET_CATEGORY = new Category("WORKING_SET");
+
+    /** A {@link Category} to represent the FREQUENT standby bucket. */
+    private static final Category FREQUENT_BUCKET_CATEGORY = new Category("FREQUENT");
+
+    /** A {@link Category} to represent the RARE standby bucket. */
+    private static final Category RARE_BUCKET_CATEGORY = new Category("RARE");
+
+    private CountQuotaTracker mQuotaTracker;
+    private final CategorizerForTest mCategorizer = new CategorizerForTest();
+    private final InjectorForTest mInjector = new InjectorForTest();
+    private final TestQuotaChangeListener mQuotaChangeListener = new TestQuotaChangeListener();
+    private BroadcastReceiver mReceiver;
+    private MockitoSession mMockingSession;
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private Context mContext;
+
+    static class CategorizerForTest implements Categorizer {
+        private Category mCategoryToUse = SINGLE_CATEGORY;
+
+        @Override
+        public Category getCategory(int userId,
+                String packageName, String tag) {
+            return mCategoryToUse;
+        }
+    }
+
+    private static class InjectorForTest extends QuotaTracker.Injector {
+        private long mElapsedTime = SystemClock.elapsedRealtime();
+
+        @Override
+        long getElapsedRealtime() {
+            return mElapsedTime;
+        }
+
+        @Override
+        boolean isAlarmManagerReady() {
+            return true;
+        }
+    }
+
+    private static class TestQuotaChangeListener implements QuotaChangeListener {
+
+        @Override
+        public void onQuotaStateChanged(int userId, String packageName, String tag) {
+
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+
+        when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
+        when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
+
+        // Freeze the clocks at 24 hours after this moment in time. Several tests create sessions
+        // in the past, and QuotaController sometimes floors values at 0, so if the test time
+        // causes sessions with negative timestamps, they will fail.
+        advanceElapsedClock(24 * HOUR_IN_MILLIS);
+
+        // Initialize real objects.
+        // Capture the listeners.
+        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        mQuotaTracker = new CountQuotaTracker(mContext, mCategorizer, mInjector);
+        mQuotaTracker.setEnabled(true);
+        mQuotaTracker.setQuotaFree(false);
+        mQuotaTracker.registerQuotaChangeListener(mQuotaChangeListener);
+        verify(mContext, atLeastOnce()).registerReceiverAsUser(
+                receiverCaptor.capture(), eq(UserHandle.ALL), any(), any(), any());
+        mReceiver = receiverCaptor.getValue();
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    /**
+     * Returns true if the two {@link LongArrayQueue}s have the same size and the same elements in
+     * the same order.
+     */
+    private static boolean longArrayQueueEquals(LongArrayQueue queue1, LongArrayQueue queue2) {
+        if (queue1 == queue2) {
+            return true;
+        } else if (queue1 == null || queue2 == null) {
+            return false;
+        }
+        if (queue1.size() == queue2.size()) {
+            for (int i = 0; i < queue1.size(); ++i) {
+                if (queue1.get(i) != queue2.get(i)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void advanceElapsedClock(long incrementMs) {
+        mInjector.mElapsedTime += incrementMs;
+    }
+
+    private void logEvents(int count) {
+        logEvents(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, count);
+    }
+
+    private void logEvents(int userId, String pkgName, String tag, int count) {
+        for (int i = 0; i < count; ++i) {
+            mQuotaTracker.noteEvent(userId, pkgName, tag);
+        }
+    }
+
+    private void logEventAt(long timeElapsed) {
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, timeElapsed);
+    }
+
+    private void logEventAt(int userId, String pkgName, String tag, long timeElapsed) {
+        long now = mInjector.getElapsedRealtime();
+        mInjector.mElapsedTime = timeElapsed;
+        mQuotaTracker.noteEvent(userId, pkgName, tag);
+        mInjector.mElapsedTime = now;
+    }
+
+    private void logEventsAt(int userId, String pkgName, String tag, long timeElapsed, int count) {
+        for (int i = 0; i < count; ++i) {
+            logEventAt(userId, pkgName, tag, timeElapsed);
+        }
+    }
+
+    @Test
+    public void testDeleteObsoleteEventsLocked() {
+        // Count window size should only apply to event list.
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 7, 2 * HOUR_IN_MILLIS);
+
+        final long now = mInjector.getElapsedRealtime();
+
+        logEventAt(now - 6 * HOUR_IN_MILLIS);
+        logEventAt(now - 5 * HOUR_IN_MILLIS);
+        logEventAt(now - 4 * HOUR_IN_MILLIS);
+        logEventAt(now - 3 * HOUR_IN_MILLIS);
+        logEventAt(now - 2 * HOUR_IN_MILLIS);
+        logEventAt(now - HOUR_IN_MILLIS);
+        logEventAt(now - 1);
+
+        LongArrayQueue expectedEvents = new LongArrayQueue();
+        expectedEvents.addLast(now - HOUR_IN_MILLIS);
+        expectedEvents.addLast(now - 1);
+
+        mQuotaTracker.deleteObsoleteEventsLocked();
+
+        LongArrayQueue remainingEvents = mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE,
+                TEST_TAG);
+        assertTrue(longArrayQueueEquals(expectedEvents, remainingEvents));
+    }
+
+    @Test
+    public void testAppRemoval() {
+        final long now = mInjector.getElapsedRealtime();
+        logEventAt(TEST_USER_ID, "com.android.test.remove", "tag1", now - (6 * HOUR_IN_MILLIS));
+        logEventAt(TEST_USER_ID, "com.android.test.remove", "tag2",
+                now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS));
+        logEventAt(TEST_USER_ID, "com.android.test.remove", "tag3", now - (HOUR_IN_MILLIS));
+        // Test that another app isn't affected.
+        LongArrayQueue expected1 = new LongArrayQueue();
+        expected1.addLast(now - 10 * MINUTE_IN_MILLIS);
+        LongArrayQueue expected2 = new LongArrayQueue();
+        expected2.addLast(now - 70 * MINUTE_IN_MILLIS);
+        logEventAt(TEST_USER_ID, "com.android.test.stay", "tag1", now - 10 * MINUTE_IN_MILLIS);
+        logEventAt(TEST_USER_ID, "com.android.test.stay", "tag2", now - 70 * MINUTE_IN_MILLIS);
+
+        Intent removal = new Intent(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+                Uri.fromParts("package", "com.android.test.remove", null));
+        removal.putExtra(Intent.EXTRA_UID, TEST_UID);
+        mReceiver.onReceive(mContext, removal);
+        assertNull(
+                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag1"));
+        assertNull(
+                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag2"));
+        assertNull(
+                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.remove", "tag3"));
+        assertTrue(longArrayQueueEquals(expected1,
+                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag1")));
+        assertTrue(longArrayQueueEquals(expected2,
+                mQuotaTracker.getEvents(TEST_USER_ID, "com.android.test.stay", "tag2")));
+    }
+
+    @Test
+    public void testUserRemoval() {
+        final long now = mInjector.getElapsedRealtime();
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, "tag1", now - (6 * HOUR_IN_MILLIS));
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, "tag2",
+                now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS));
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, "tag3", now - (HOUR_IN_MILLIS));
+        // Test that another user isn't affected.
+        LongArrayQueue expected = new LongArrayQueue();
+        expected.addLast(now - (70 * MINUTE_IN_MILLIS));
+        expected.addLast(now - (10 * MINUTE_IN_MILLIS));
+        logEventAt(10, TEST_PACKAGE, "tag4", now - (70 * MINUTE_IN_MILLIS));
+        logEventAt(10, TEST_PACKAGE, "tag4", now - 10 * MINUTE_IN_MILLIS);
+
+        Intent removal = new Intent(Intent.ACTION_USER_REMOVED);
+        removal.putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID);
+        mReceiver.onReceive(mContext, removal);
+        assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag1"));
+        assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag2"));
+        assertNull(mQuotaTracker.getEvents(TEST_USER_ID, TEST_PACKAGE, "tag3"));
+        longArrayQueueEquals(expected, mQuotaTracker.getEvents(10, TEST_PACKAGE, "tag4"));
+    }
+
+    @Test
+    public void testUpdateExecutionStatsLocked_NoTimer() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 3, 24 * HOUR_IN_MILLIS);
+        final long now = mInjector.getElapsedRealtime();
+
+        // Added in chronological order.
+        logEventAt(now - 4 * HOUR_IN_MILLIS);
+        logEventAt(now - HOUR_IN_MILLIS);
+        logEventAt(now - 5 * MINUTE_IN_MILLIS);
+        logEventAt(now - MINUTE_IN_MILLIS);
+
+        // Test an app that hasn't had any activity.
+        ExecutionStats expectedStats = new ExecutionStats();
+        ExecutionStats inputStats = new ExecutionStats();
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
+        inputStats.countLimit = expectedStats.countLimit = 3;
+        // Invalid time is now +24 hours since there are no sessions at all for the app.
+        expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, "com.android.test.not.run", TEST_TAG,
+                inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        // Now test app that has had activity.
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = MINUTE_IN_MILLIS;
+        // Invalid time is now since there was an event exactly windowSizeMs ago.
+        expectedStats.expirationTimeElapsed = now;
+        expectedStats.countInWindow = 1;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 3 * MINUTE_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 2 * MINUTE_IN_MILLIS;
+        expectedStats.countInWindow = 1;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 4 * MINUTE_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 3 * MINUTE_IN_MILLIS;
+        expectedStats.countInWindow = 1;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 49 * MINUTE_IN_MILLIS;
+        // Invalid time is now +44 minutes since the earliest session in the window is now-5
+        // minutes.
+        expectedStats.expirationTimeElapsed = now + 44 * MINUTE_IN_MILLIS;
+        expectedStats.countInWindow = 2;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 50 * MINUTE_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 45 * MINUTE_IN_MILLIS;
+        expectedStats.countInWindow = 2;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = HOUR_IN_MILLIS;
+        // Invalid time is now since the event is at the very edge of the window
+        // cutoff time.
+        expectedStats.expirationTimeElapsed = now;
+        expectedStats.countInWindow = 3;
+        // App is at event count limit but the oldest session is at the edge of the window, so
+        // in quota time is now.
+        expectedStats.inQuotaTimeElapsed = now;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.countInWindow = 3;
+        expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 5 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.countInWindow = 4;
+        expectedStats.inQuotaTimeElapsed = now + 4 * HOUR_IN_MILLIS;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+
+        inputStats.windowSizeMs = expectedStats.windowSizeMs = 6 * HOUR_IN_MILLIS;
+        expectedStats.expirationTimeElapsed = now + 2 * HOUR_IN_MILLIS;
+        expectedStats.countInWindow = 4;
+        expectedStats.inQuotaTimeElapsed = now + 5 * HOUR_IN_MILLIS;
+        mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, inputStats);
+        assertEquals(expectedStats, inputStats);
+    }
+
+    /**
+     * Tests that getExecutionStatsLocked returns the correct stats.
+     */
+    @Test
+    public void testGetExecutionStatsLocked_Values() {
+        // The handler could cause changes to the cached stats, so prevent it from operating in
+        // this test.
+        Handler handler = mQuotaTracker.getHandler();
+        spyOn(handler);
+        doNothing().when(handler).handleMessage(any());
+
+        mQuotaTracker.setCountLimit(RARE_BUCKET_CATEGORY, 3, 24 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(FREQUENT_BUCKET_CATEGORY, 4, 8 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(WORKING_SET_BUCKET_CATEGORY, 9, 2 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(ACTIVE_BUCKET_CATEGORY, 10, 10 * MINUTE_IN_MILLIS);
+
+        final long now = mInjector.getElapsedRealtime();
+
+        logEventAt(now - 23 * HOUR_IN_MILLIS);
+        logEventAt(now - 7 * HOUR_IN_MILLIS);
+        logEventAt(now - 5 * HOUR_IN_MILLIS);
+        logEventAt(now - 2 * HOUR_IN_MILLIS);
+        logEventAt(now - 5 * MINUTE_IN_MILLIS);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        // Active
+        expectedStats.expirationTimeElapsed = now + 5 * MINUTE_IN_MILLIS;
+        expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
+        expectedStats.countLimit = 10;
+        expectedStats.countInWindow = 1;
+        mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+        assertEquals(expectedStats,
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+
+        // Working
+        expectedStats.expirationTimeElapsed = now;
+        expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+        expectedStats.countLimit = 9;
+        expectedStats.countInWindow = 2;
+        mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+        assertEquals(expectedStats,
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+
+        // Frequent
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = 8 * HOUR_IN_MILLIS;
+        expectedStats.countLimit = 4;
+        expectedStats.countInWindow = 4;
+        expectedStats.inQuotaTimeElapsed = now + HOUR_IN_MILLIS;
+        mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+        assertEquals(expectedStats,
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+
+        // Rare
+        expectedStats.expirationTimeElapsed = now + HOUR_IN_MILLIS;
+        expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
+        expectedStats.countLimit = 3;
+        expectedStats.countInWindow = 5;
+        expectedStats.inQuotaTimeElapsed = now + 19 * HOUR_IN_MILLIS;
+        mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
+        assertEquals(expectedStats,
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+    }
+
+    /**
+     * Tests that getExecutionStatsLocked returns the correct stats soon after device startup.
+     */
+    @Test
+    public void testGetExecutionStatsLocked_Values_BeginningOfTime() {
+        // Set time to 3 minutes after boot.
+        mInjector.mElapsedTime = 3 * MINUTE_IN_MILLIS;
+
+        logEventAt(30_000);
+        logEventAt(MINUTE_IN_MILLIS);
+        logEventAt(2 * MINUTE_IN_MILLIS);
+
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+        ExecutionStats expectedStats = new ExecutionStats();
+
+        expectedStats.windowSizeMs = 2 * HOUR_IN_MILLIS;
+        expectedStats.countLimit = 10;
+        expectedStats.countInWindow = 3;
+        expectedStats.expirationTimeElapsed = 2 * HOUR_IN_MILLIS + 30_000;
+        assertEquals(expectedStats,
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+    }
+
+    @Test
+    public void testisWithinQuota_GlobalQuotaFree() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 2 * HOUR_IN_MILLIS);
+        mQuotaTracker.setQuotaFree(true);
+        assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, null));
+        assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, "com.android.random.app", null));
+    }
+
+    @Test
+    public void testisWithinQuota_UptcQuotaFree() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 2 * HOUR_IN_MILLIS);
+        mQuotaTracker.setQuotaFree(TEST_USER_ID, TEST_PACKAGE, true);
+        assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, null));
+        assertFalse(
+                mQuotaTracker.isWithinQuota(TEST_USER_ID, "com.android.random.app", null));
+    }
+
+    @Test
+    public void testisWithinQuota_UnderCount() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+        logEvents(5);
+        assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+    }
+
+    @Test
+    public void testisWithinQuota_OverCount() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 25, HOUR_IN_MILLIS);
+        logEvents(TEST_USER_ID, "com.android.test.spam", TEST_TAG, 30);
+        assertFalse(mQuotaTracker.isWithinQuota(TEST_USER_ID, "com.android.test.spam", TEST_TAG));
+    }
+
+    @Test
+    public void testisWithinQuota_EqualsCount() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 25, HOUR_IN_MILLIS);
+        logEvents(25);
+        assertFalse(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+    }
+
+    @Test
+    public void testisWithinQuota_DifferentCategories() {
+        mQuotaTracker.setCountLimit(RARE_BUCKET_CATEGORY, 3, 24 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(FREQUENT_BUCKET_CATEGORY, 4, 24 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(WORKING_SET_BUCKET_CATEGORY, 5, 24 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(ACTIVE_BUCKET_CATEGORY, 6, 24 * HOUR_IN_MILLIS);
+
+        for (int i = 0; i < 7; ++i) {
+            logEvents(1);
+
+            mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
+            assertEquals("Rare has incorrect quota status with " + (i + 1) + " events",
+                    i < 2,
+                    mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+            mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+            assertEquals("Frequent has incorrect quota status with " + (i + 1) + " events",
+                    i < 3,
+                    mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+            mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+            assertEquals("Working has incorrect quota status with " + (i + 1) + " events",
+                    i < 4,
+                    mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+            mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+            assertEquals("Active has incorrect quota status with " + (i + 1) + " events",
+                    i < 5,
+                    mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+        }
+    }
+
+    @Test
+    public void testMaybeScheduleCleanupAlarmLocked() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 5, 24 * HOUR_IN_MILLIS);
+
+        // No sessions saved yet.
+        mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_CLEANUP), any(), any());
+
+        // Test with only one timing session saved.
+        final long now = mInjector.getElapsedRealtime();
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 6 * HOUR_IN_MILLIS);
+        mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+
+        // Test with new (more recent) timing sessions saved. AlarmManger shouldn't be called again.
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS);
+        logEventAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS);
+        mQuotaTracker.maybeScheduleCleanupAlarmLocked();
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(now + 18 * HOUR_IN_MILLIS), eq(TAG_CLEANUP), any(), any());
+    }
+
+    /**
+     * Tests that maybeScheduleStartAlarm schedules an alarm for the right time.
+     */
+    @Test
+    public void testMaybeScheduleStartAlarmLocked() {
+        // logEvent calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaTracker);
+        doNothing().when(mQuotaTracker).maybeScheduleCleanupAlarmLocked();
+
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 8 * HOUR_IN_MILLIS);
+
+        // No sessions saved yet.
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions out of window.
+        final long now = mInjector.getElapsedRealtime();
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 10 * HOUR_IN_MILLIS, 20);
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test with timing sessions in window but still in quota.
+        final long start = now - (6 * HOUR_IN_MILLIS);
+        final long expectedAlarmTime = start + 8 * HOUR_IN_MILLIS;
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, start, 5);
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Add some more sessions, but still in quota.
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 3 * HOUR_IN_MILLIS, 1);
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 3);
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        verify(mAlarmManager, never()).set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Test when out of quota.
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - HOUR_IN_MILLIS, 1);
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // Alarm already scheduled, so make sure it's not scheduled again.
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        verify(mAlarmManager, times(1))
+                .set(anyInt(), eq(expectedAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
+    /** Tests that the start alarm is properly rescheduled if the app's category is changed. */
+    @Test
+    public void testMaybeScheduleStartAlarmLocked_CategoryChange() {
+        // logEvent calls maybeScheduleCleanupAlarmLocked which interferes with these tests
+        // because it schedules an alarm too. Prevent it from doing so.
+        spyOn(mQuotaTracker);
+        doNothing().when(mQuotaTracker).maybeScheduleCleanupAlarmLocked();
+
+        mQuotaTracker.setCountLimit(RARE_BUCKET_CATEGORY, 10, 24 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(FREQUENT_BUCKET_CATEGORY, 10, 8 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(WORKING_SET_BUCKET_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+        mQuotaTracker.setCountLimit(ACTIVE_BUCKET_CATEGORY, 10, 10 * MINUTE_IN_MILLIS);
+
+        final long now = mInjector.getElapsedRealtime();
+
+        // Affects rare bucket
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 12 * HOUR_IN_MILLIS, 9);
+        // Affects frequent and rare buckets
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 4 * HOUR_IN_MILLIS, 4);
+        // Affects working, frequent, and rare buckets
+        final long outOfQuotaTime = now - HOUR_IN_MILLIS;
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, outOfQuotaTime, 7);
+        // Affects all buckets
+        logEventsAt(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, now - 5 * MINUTE_IN_MILLIS, 3);
+
+        InOrder inOrder = inOrder(mAlarmManager);
+
+        // Start in ACTIVE bucket.
+        mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, never())
+                .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+        inOrder.verify(mAlarmManager, never()).cancel(any(AlarmManager.OnAlarmListener.class));
+
+        // And down from there.
+        final long expectedWorkingAlarmTime = outOfQuotaTime + (2 * HOUR_IN_MILLIS);
+        mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        final long expectedFrequentAlarmTime = outOfQuotaTime + (8 * HOUR_IN_MILLIS);
+        mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        final long expectedRareAlarmTime = outOfQuotaTime + (24 * HOUR_IN_MILLIS);
+        mCategorizer.mCategoryToUse = RARE_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(expectedRareAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        // And back up again.
+        mCategorizer.mCategoryToUse = FREQUENT_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(expectedFrequentAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        mCategorizer.mCategoryToUse = WORKING_SET_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, timeout(1000).times(1))
+                .set(anyInt(), eq(expectedWorkingAlarmTime), eq(TAG_QUOTA_CHECK), any(), any());
+
+        mCategorizer.mCategoryToUse = ACTIVE_BUCKET_CATEGORY;
+        mQuotaTracker.maybeScheduleStartAlarmLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        inOrder.verify(mAlarmManager, timeout(1000).times(1))
+                .cancel(any(AlarmManager.OnAlarmListener.class));
+        inOrder.verify(mAlarmManager, timeout(1000).times(0))
+                .set(anyInt(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
+    }
+
+    @Test
+    public void testConstantsUpdating_ValidValues() {
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 60_000);
+        assertEquals(0, mQuotaTracker.getLimit(SINGLE_CATEGORY));
+        assertEquals(60_000, mQuotaTracker.getWindowSizeMs(SINGLE_CATEGORY));
+    }
+
+    @Test
+    public void testConstantsUpdating_InvalidValues() {
+        // Test negatives.
+        try {
+            mQuotaTracker.setCountLimit(SINGLE_CATEGORY, -1, 5000);
+            fail("Negative count limit didn't throw an exception");
+        } catch (IllegalArgumentException e) {
+            // Success
+        }
+        try {
+            mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 1, -1);
+            fail("Negative count window size didn't throw an exception");
+        } catch (IllegalArgumentException e) {
+            // Success
+        }
+
+        // Test window sizes too low.
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 1);
+        assertEquals(MIN_WINDOW_SIZE_MS, mQuotaTracker.getWindowSizeMs(SINGLE_CATEGORY));
+
+        // Test window sizes too high.
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 0, 365 * 24 * HOUR_IN_MILLIS);
+        assertEquals(MAX_WINDOW_SIZE_MS, mQuotaTracker.getWindowSizeMs(SINGLE_CATEGORY));
+    }
+
+    /** Tests that events aren't counted when global quota is free. */
+    @Test
+    public void testLogEvent_GlobalQuotaFree() {
+        mQuotaTracker.setQuotaFree(true);
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+        ExecutionStats stats =
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        assertEquals(0, stats.countInWindow);
+
+        for (int i = 0; i < 10; ++i) {
+            mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+            advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            assertEquals(0, stats.countInWindow);
+        }
+    }
+
+    /**
+     * Tests that events are counted when global quota is not free.
+     */
+    @Test
+    public void testLogEvent_GlobalQuotaNotFree() {
+        mQuotaTracker.setQuotaFree(false);
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+        ExecutionStats stats =
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        assertEquals(0, stats.countInWindow);
+
+        for (int i = 0; i < 10; ++i) {
+            mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+            advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            assertEquals(i + 1, stats.countInWindow);
+        }
+    }
+
+    /** Tests that events aren't counted when the uptc quota is free. */
+    @Test
+    public void testLogEvent_UptcQuotaFree() {
+        mQuotaTracker.setQuotaFree(TEST_USER_ID, TEST_PACKAGE, true);
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+        ExecutionStats stats =
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        assertEquals(0, stats.countInWindow);
+
+        for (int i = 0; i < 10; ++i) {
+            mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+            advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            assertEquals(0, stats.countInWindow);
+        }
+    }
+
+    /**
+     * Tests that events are counted when UPTC quota is not free.
+     */
+    @Test
+    public void testLogEvent_UptcQuotaNotFree() {
+        mQuotaTracker.setQuotaFree(TEST_USER_ID, TEST_PACKAGE, false);
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+
+        ExecutionStats stats =
+                mQuotaTracker.getExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        assertEquals(0, stats.countInWindow);
+
+        for (int i = 0; i < 10; ++i) {
+            mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+            advanceElapsedClock(10 * SECOND_IN_MILLIS);
+
+            mQuotaTracker.updateExecutionStatsLocked(TEST_USER_ID, TEST_PACKAGE, TEST_TAG, stats);
+            assertEquals(i + 1, stats.countInWindow);
+        }
+    }
+
+    /**
+     * Tests that QuotaChangeListeners are notified when a UPTC reaches its count quota.
+     */
+    @Test
+    public void testTracking_OutOfQuota() {
+        spyOn(mQuotaChangeListener);
+
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 10, 2 * HOUR_IN_MILLIS);
+        logEvents(9);
+
+        mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+
+        // Wait for some extra time to allow for processing.
+        verify(mQuotaChangeListener, timeout(3 * SECOND_IN_MILLIS).times(1))
+                .onQuotaStateChanged(eq(TEST_USER_ID), eq(TEST_PACKAGE), eq(TEST_TAG));
+        assertFalse(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+    }
+
+    /**
+     * Tests that QuotaChangeListeners are not incorrectly notified after a UPTC event is logged
+     * quota times.
+     */
+    @Test
+    public void testTracking_InQuota() {
+        spyOn(mQuotaChangeListener);
+
+        mQuotaTracker.setCountLimit(SINGLE_CATEGORY, 5, MINUTE_IN_MILLIS);
+
+        // Log an event once per minute. This is well below the quota, so listeners should not be
+        // notified.
+        for (int i = 0; i < 10; i++) {
+            advanceElapsedClock(MINUTE_IN_MILLIS);
+            mQuotaTracker.noteEvent(TEST_USER_ID, TEST_PACKAGE, TEST_TAG);
+        }
+
+        // Wait for some extra time to allow for processing.
+        verify(mQuotaChangeListener, timeout(3 * SECOND_IN_MILLIS).times(0))
+                .onQuotaStateChanged(eq(TEST_USER_ID), eq(TEST_PACKAGE), eq(TEST_TAG));
+        assertTrue(mQuotaTracker.isWithinQuota(TEST_USER_ID, TEST_PACKAGE, TEST_TAG));
+    }
+}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 015e574f2..ace15eb 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -26,6 +26,7 @@
         "services.core",
         "services.devicepolicy",
         "services.net",
+        "services.people",
         "services.usage",
         "guava",
         "androidx.test.core",
@@ -43,6 +44,10 @@
         "servicestests-utils",
         "service-appsearch",
         "service-jobscheduler",
+        // TODO: remove once Android migrates to JUnit 4.12,
+        // which provides assertThrows
+        "testng",
+
     ],
 
     aidl: {
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index d34f783..bbc6bdb 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -26,6 +26,11 @@
         <option name="test-file-name" value="SimpleServiceTestApp.apk" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="AppIntegrityManagerServiceTestApp.apk->/data/local/tmp/AppIntegrityManagerServiceTestApp.apk" />
+    </target_preparer>
+
     <option name="test-tag" value="FrameworksServicesTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
deleted file mode 100644
index 6345c98..0000000
--- a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/test.apk
+++ /dev/null
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
index e9c5ce7..d5483ff 100644
--- a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
@@ -19,8 +19,11 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.location.Country;
 import android.location.CountryListener;
 import android.location.ICountryListener;
@@ -31,6 +34,10 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.internal.R;
+import com.android.server.location.ComprehensiveCountryDetector;
+import com.android.server.location.CustomCountryDetectorTestClass;
+
 import com.google.common.truth.Expect;
 
 import org.junit.Before;
@@ -38,12 +45,18 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.Spy;
 import org.mockito.junit.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class CountryDetectorServiceTest {
 
+    private static final String VALID_CUSTOM_TEST_CLASS =
+            "com.android.server.location.CustomCountryDetectorTestClass";
+    private static final String INVALID_CUSTOM_TEST_CLASS =
+            "com.android.server.location.MissingCountryDetectorTestClass";
+
     private static class CountryListenerTester extends ICountryListener.Stub {
         private Country mCountry;
 
@@ -83,12 +96,11 @@
         }
     }
 
-    @Rule
-    public final Expect expect = Expect.create();
-    @Spy
-    private Context mContext = ApplicationProvider.getApplicationContext();
-    @Spy
-    private Handler mHandler = new Handler(Looper.myLooper());
+    @Rule public final Expect expect = Expect.create();
+    @Spy private Context mContext = ApplicationProvider.getApplicationContext();
+    @Spy private Handler mHandler = new Handler(Looper.myLooper());
+    @Mock private Resources mResources;
+
     private CountryDetectorServiceTester mCountryDetectorService;
 
     @BeforeClass
@@ -108,10 +120,12 @@
             message.getCallback().run();
             return true;
         }).when(mHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+        doReturn(mResources).when(mContext).getResources();
     }
 
     @Test
-    public void countryListener_add_successful() throws RemoteException {
+    public void addCountryListener_validListener_listenerAdded() throws RemoteException {
         CountryListenerTester countryListener = new CountryListenerTester();
 
         mCountryDetectorService.systemRunning();
@@ -122,7 +136,7 @@
     }
 
     @Test
-    public void countryListener_remove_successful() throws RemoteException {
+    public void removeCountryListener_validListener_listenerRemoved() throws RemoteException {
         CountryListenerTester countryListener = new CountryListenerTester();
 
         mCountryDetectorService.systemRunning();
@@ -133,8 +147,31 @@
         expect.that(mCountryDetectorService.isListenerSet()).isFalse();
     }
 
+    @Test(expected = RemoteException.class)
+    public void addCountryListener_serviceNotReady_throwsException() throws RemoteException {
+        CountryListenerTester countryListener = new CountryListenerTester();
+
+        expect.that(mCountryDetectorService.isSystemReady()).isFalse();
+        mCountryDetectorService.addCountryListener(countryListener);
+    }
+
+    @Test(expected = RemoteException.class)
+    public void removeCountryListener_serviceNotReady_throwsException() throws RemoteException {
+        CountryListenerTester countryListener = new CountryListenerTester();
+
+        expect.that(mCountryDetectorService.isSystemReady()).isFalse();
+        mCountryDetectorService.removeCountryListener(countryListener);
+    }
+
     @Test
-    public void countryListener_notify_successful() throws RemoteException {
+    public void detectCountry_serviceNotReady_returnNull() {
+        expect.that(mCountryDetectorService.isSystemReady()).isFalse();
+
+        expect.that(mCountryDetectorService.detectCountry()).isNull();
+    }
+
+    @Test
+    public void notifyReceivers_twoListenersRegistered_bothNotified() throws RemoteException {
         CountryListenerTester countryListenerA = new CountryListenerTester();
         CountryListenerTester countryListenerB = new CountryListenerTester();
         Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
@@ -151,4 +188,26 @@
         expect.that(countryListenerA.getCountry().equalsIgnoreSource(country)).isTrue();
         expect.that(countryListenerB.getCountry().equalsIgnoreSource(country)).isTrue();
     }
+
+    @Test
+    public void initialize_deviceWithCustomDetector_useCustomDetectorClass() {
+        when(mResources.getString(R.string.config_customCountryDetector))
+                .thenReturn(VALID_CUSTOM_TEST_CLASS);
+
+        mCountryDetectorService.initialize();
+
+        expect.that(mCountryDetectorService.getCountryDetector())
+                .isInstanceOf(CustomCountryDetectorTestClass.class);
+    }
+
+    @Test
+    public void initialize_deviceWithInvalidCustomDetector_useDefaultDetector() {
+        when(mResources.getString(R.string.config_customCountryDetector))
+                .thenReturn(INVALID_CUSTOM_TEST_CLASS);
+
+        mCountryDetectorService.initialize();
+
+        expect.that(mCountryDetectorService.getCountryDetector())
+                .isInstanceOf(ComprehensiveCountryDetector.class);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 2ce17a1..f8bcff5 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -485,7 +485,7 @@
     @Test
     public void testDispatchUids_dispatchNeededChanges() throws RemoteException {
         when(mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS, Process.myUid(), null,
-                null)).thenReturn(AppOpsManager.MODE_ALLOWED);
+                null, false, null)).thenReturn(AppOpsManager.MODE_ALLOWED);
 
         final int[] changesToObserve = {
             ActivityManager.UID_OBSERVER_PROCSTATE,
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
new file mode 100644
index 0000000..fa35e3f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup.internal;
+
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.assertTrue;
+
+import android.os.HandlerThread;
+import android.os.Message;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.UserBackupManagerService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupHandlerTest {
+    private static final int MESSAGE_TIMEOUT_MINUTES = 1;
+
+    @Mock private UserBackupManagerService mUserBackupManagerService;
+    @Mock private BackupAgentTimeoutParameters mTimeoutParameters;
+
+    private HandlerThread mHandlerThread;
+    private CountDownLatch mCountDownLatch;
+    private boolean mExceptionPropagated;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(/* testClass */ this);
+        when(mUserBackupManagerService.getAgentTimeoutParameters()).thenReturn(mTimeoutParameters);
+
+        mExceptionPropagated = false;
+        mCountDownLatch = new CountDownLatch(/* count */ 1);
+        mHandlerThread = new HandlerThread("BackupHandlerTestThread");
+        mHandlerThread.start();
+    }
+
+    @After
+    public void tearDown() {
+        mHandlerThread.quit();
+    }
+
+    @Test
+    public void testSendMessage_propagatesExceptions() throws Exception {
+        BackupHandler handler = new TestBackupHandler(/* shouldStop */ false);
+        handler.sendMessage(getMessage());
+        mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+        assertTrue(mExceptionPropagated);
+    }
+
+    @Test
+    public void testPost_propagatesExceptions() throws Exception {
+        BackupHandler handler = new TestBackupHandler(/* shouldStop */ false);
+        handler.post(() -> {});
+        mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+        assertTrue(mExceptionPropagated);
+    }
+
+    @Test
+    public void testSendMessage_stopping_doesntPropagateExceptions() throws Exception {
+        BackupHandler handler = new TestBackupHandler(/* shouldStop */ true);
+        handler.sendMessage(getMessage());
+        mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+        assertFalse(mExceptionPropagated);
+    }
+
+    @Test
+    public void testPost_stopping_doesntPropagateExceptions() throws Exception {
+        BackupHandler handler = new TestBackupHandler(/* shouldStop */ true);
+        handler.post(() -> {});
+        mCountDownLatch.await(MESSAGE_TIMEOUT_MINUTES, TimeUnit.MINUTES);
+
+        assertFalse(mExceptionPropagated);
+    }
+
+    private static Message getMessage() {
+        Message message = Message.obtain();
+        message.what = -1;
+        return message;
+    }
+
+    private class TestBackupHandler extends BackupHandler  {
+        private final boolean mShouldStop;
+
+        TestBackupHandler(boolean shouldStop) {
+            super(mUserBackupManagerService, mHandlerThread);
+
+            mShouldStop = shouldStop;
+        }
+
+        @Override
+        public void dispatchMessage(Message msg) {
+            try {
+                super.dispatchMessage(msg);
+            } catch (Exception e) {
+                mExceptionPropagated = true;
+            } finally {
+                mCountDownLatch.countDown();
+            }
+        }
+
+        @Override
+        void dispatchMessageInternal(Message msg) {
+            mIsStopping = mShouldStop;
+            throw new RuntimeException();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 4fcfa32..a16e14f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -104,6 +104,7 @@
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.mockito.Mockito;
+import org.mockito.internal.util.collections.Sets;
 import org.mockito.stubbing.Answer;
 
 import java.io.File;
@@ -3659,6 +3660,25 @@
         assertExpectException(SecurityException.class, null, () -> dpm.setTime(admin1, 0));
     }
 
+    public void testSetTimeWithPOOfOrganizationOwnedDevice() throws Exception {
+        setupProfileOwner();
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+        dpm.setTime(admin1, 0);
+
+        BaseMatcher<ManualTimeSuggestion> hasZeroTime = new BaseMatcher<ManualTimeSuggestion>() {
+            @Override
+            public boolean matches(Object item) {
+                final ManualTimeSuggestion suggestion = (ManualTimeSuggestion) item;
+                return suggestion.getUtcTime().getValue() == 0;
+            }
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("ManualTimeSuggestion{utcTime.value=0}");
+            }
+        };
+        verify(getServices().timeDetector).suggestManualTime(argThat(hasZeroTime));
+    }
+
     public void testSetTimeWithAutoTimeOn() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
@@ -3682,6 +3702,15 @@
                 () -> dpm.setTimeZone(admin1, "Asia/Shanghai"));
     }
 
+    public void testSetTimeZoneWithPOOfOrganizationOwnedDevice() throws Exception {
+        setupProfileOwner();
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+        dpm.setTimeZone(admin1, "Asia/Shanghai");
+        ManualTimeZoneSuggestion suggestion =
+                TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
+        verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
+    }
+
     public void testSetTimeZoneWithAutoTimeZoneOn() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
@@ -5496,6 +5525,36 @@
         assertTrue(dpm.isPackageAllowedToAccessCalendar(testPackage));
     }
 
+    public void testSetProtectedPackages_asDO() throws Exception {
+        final List<String> testPackages = new ArrayList<>();
+        testPackages.add("package_1");
+        testPackages.add("package_2");
+
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+        setDeviceOwner();
+
+        dpm.setProtectedPackages(admin1, testPackages);
+
+        verify(getServices().packageManagerInternal).setDeviceOwnerProtectedPackages(testPackages);
+
+        assertEquals(testPackages, dpm.getProtectedPackages(admin1));
+    }
+
+    public void testSetProtectedPackages_failingAsPO() throws Exception {
+        final List<String> testPackages = new ArrayList<>();
+        testPackages.add("package_1");
+        testPackages.add("package_2");
+
+        mContext.callerPermissions.add(android.Manifest.permission.MANAGE_DEVICE_ADMINS);
+        setAsProfileOwner(admin1);
+
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.setProtectedPackages(admin1, testPackages));
+
+        assertExpectException(SecurityException.class, /* messageRegex= */ null,
+                () -> dpm.getProtectedPackages(admin1));
+    }
+
     private void configureProfileOwnerOfOrgOwnedDevice(ComponentName who, int userId) {
         when(getServices().userManager.getProfileParent(eq(UserHandle.of(userId))))
                 .thenReturn(UserHandle.SYSTEM);
@@ -5542,6 +5601,57 @@
         assertEquals(packages, dpm.getCrossProfilePackages(admin1));
     }
 
+    public void testGetAllCrossProfilePackages_notSet_returnsEmpty() throws Exception {
+        addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+
+        setCrossProfileAppsList();
+
+        assertTrue(dpm.getAllCrossProfilePackages().isEmpty());
+    }
+
+    public void testGetAllCrossProfilePackages_notSet_dpmsReinitialized_returnsEmpty()
+            throws Exception {
+        addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+
+        setCrossProfileAppsList();
+        initializeDpms();
+
+        assertTrue(dpm.getAllCrossProfilePackages().isEmpty());
+    }
+
+    public void testGetAllCrossProfilePackages_whenSet_returnsCombinedSet() throws Exception {
+        addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+        final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
+
+        dpm.setCrossProfilePackages(admin1, packages);
+        setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+
+        assertEquals(Sets.newSet(
+                        "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"),
+                dpm.getAllCrossProfilePackages());
+
+    }
+
+    public void testGetAllCrossProfilePackages_whenSet_dpmsReinitialized_returnsCombinedSet()
+            throws Exception {
+        addManagedProfile(admin1, mServiceContext.binder.callingUid, admin1);
+        final Set<String> packages = Sets.newSet("TEST_PACKAGE", "TEST_COMMON_PACKAGE");
+
+        dpm.setCrossProfilePackages(admin1, packages);
+        setCrossProfileAppsList("TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE");
+        initializeDpms();
+
+        assertEquals(Sets.newSet(
+                "TEST_PACKAGE", "TEST_DEFAULT_PACKAGE", "TEST_COMMON_PACKAGE"),
+                dpm.getAllCrossProfilePackages());
+    }
+
+    private void setCrossProfileAppsList(String... packages) {
+        when(mContext.getResources()
+                .getStringArray(eq(R.array.cross_profile_apps)))
+                .thenReturn(packages);
+    }
+
     // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
     private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
         writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 1a67576..960f670 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -217,6 +217,8 @@
                 return mMockSystemServices.wifiManager;
             case Context.ACCOUNT_SERVICE:
                 return mMockSystemServices.accountManager;
+            case Context.TELEPHONY_SERVICE:
+                return mMockSystemServices.telephonyManager;
         }
         throw new UnsupportedOperationException();
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 17e5832..09a6819 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -17,7 +17,6 @@
 
 import com.google.common.base.Objects;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.pm.UserRestrictionsUtils;
 
 import android.content.ComponentName;
@@ -107,7 +106,7 @@
     }
 
     public static Bundle checkUserRestrictions(String... keys) {
-        final Bundle expected = DpmTestUtils.newRestrictions(Preconditions.checkNotNull(keys));
+        final Bundle expected = DpmTestUtils.newRestrictions(java.util.Objects.requireNonNull(keys));
         final Matcher<Bundle> m = new BaseMatcher<Bundle>() {
             @Override
             public boolean matches(Object item) {
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 a2376a6..604efc4 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -64,7 +65,6 @@
 import com.android.server.integrity.model.IntegrityCheckResult;
 import com.android.server.testutils.TestUtils;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -75,9 +75,6 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -86,7 +83,8 @@
 /** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
 @RunWith(AndroidJUnit4.class)
 public class AppIntegrityManagerServiceImplTest {
-    private static final String TEST_DIR = "AppIntegrityManagerServiceImplTest";
+    private static final String TEST_APP_PATH =
+            "/data/local/tmp/AppIntegrityManagerServiceTestApp.apk";
 
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
     private static final String VERSION = "version";
@@ -97,13 +95,19 @@
     private static final String INSTALLER = TEST_FRAMEWORK_PACKAGE;
     // These are obtained by running the test and checking logcat.
     private static final String APP_CERT =
-            "949ADC6CB92FF09E3784D6E9504F26F9BEAC06E60D881D55A6A81160F9CD6FD1";
+            "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
     private static final String INSTALLER_CERT =
             "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
     // We use SHA256 for package names longer than 32 characters.
     private static final String INSTALLER_SHA256 =
             "786933C28839603EB48C50B2A688DC6BE52C833627CB2731FF8466A2AE9F94CD";
 
+    private static final String PLAY_STORE_PKG = "com.android.vending";
+    private static final String ADB_INSTALLER = "adb";
+    private static final String PLAY_STORE_CERT =
+            "play_store_cert";
+    private static final String ADB_CERT = "";
+
     @org.junit.Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
 
     @Mock PackageManagerInternal mPackageManagerInternal;
@@ -122,11 +126,7 @@
 
     @Before
     public void setup() throws Exception {
-        mTestApk = File.createTempFile("TestApk", /* suffix= */ null);
-        mTestApk.deleteOnExit();
-        try (InputStream inputStream = mRealContext.getAssets().open(TEST_DIR + "/test.apk")) {
-            Files.copy(inputStream, mTestApk.toPath(), StandardCopyOption.REPLACE_EXISTING);
-        }
+        mTestApk = new File(TEST_APP_PATH);
 
         mService =
                 new AppIntegrityManagerServiceImpl(
@@ -141,11 +141,7 @@
         when(mMockContext.getPackageManager()).thenReturn(mSpyPackageManager);
         when(mMockContext.getResources()).thenReturn(mMockResources);
         when(mMockResources.getStringArray(anyInt())).thenReturn(new String[] {});
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mTestApk.delete();
+        when(mIntegrityFileManager.initialized()).thenReturn(true);
     }
 
     // This is not a test of the class, but more of a safeguard that we don't block any install in
@@ -310,10 +306,10 @@
         assertEquals(INSTALLER_CERT, appInstallMetadata.getInstallerCertificate());
         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
         assertFalse(appInstallMetadata.isPreInstalled());
-        // These are hardcoded in the test apk
+        // These are hardcoded in the test apk android manifest
         assertEquals(2, allowedInstallers.size());
-        assertEquals("cert_1", allowedInstallers.get("store_1"));
-        assertEquals("cert_2", allowedInstallers.get("store_2"));
+        assertEquals(PLAY_STORE_CERT, allowedInstallers.get(PLAY_STORE_PKG));
+        assertEquals(ADB_CERT, allowedInstallers.get(ADB_INSTALLER));
     }
 
     @Test
@@ -356,6 +352,25 @@
                         1, PackageManagerInternal.INTEGRITY_VERIFICATION_REJECT);
     }
 
+    @Test
+    public void handleBroadcast_notInitialized() throws Exception {
+        when(mIntegrityFileManager.initialized()).thenReturn(false);
+        ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mMockContext)
+                .registerReceiver(broadcastReceiverCaptor.capture(), any(), any(), any());
+        Intent intent = makeVerificationIntent();
+        when(mRuleEvaluationEngine.evaluate(any(), any())).thenReturn(IntegrityCheckResult.allow());
+
+        broadcastReceiverCaptor.getValue().onReceive(mMockContext, intent);
+        runJobInHandler();
+
+        verify(mPackageManagerInternal)
+                .setIntegrityVerificationResult(
+                        1, PackageManagerInternal.INTEGRITY_VERIFICATION_ALLOW);
+        verify(mSpyPackageManager, never()).getPackageArchiveInfo(any(), anyInt());
+    }
+
     private void whitelistUsAsRuleProvider() {
         Resources mockResources = mock(Resources.class);
         when(mockResources.getStringArray(R.array.config_integrityRuleProviderPackages))
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
new file mode 100644
index 0000000..63189e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasItems;
+import static org.junit.Assert.assertThat;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+import static org.testng.Assert.assertTrue;
+
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.AtomicFormula.IntAtomicFormula;
+import android.content.integrity.AtomicFormula.StringAtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Rule;
+import android.util.Slog;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.integrity.parser.RuleXmlParser;
+import com.android.server.integrity.serializer.RuleXmlSerializer;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/** Unit test for {@link IntegrityFileManager} */
+@RunWith(AndroidJUnit4.class)
+public class IntegrityFileManagerTest {
+    private static final String TAG = "IntegrityFileManagerTest";
+
+    private static final String VERSION = "version";
+    private static final String RULE_PROVIDER = "rule_provider";
+
+    private File mTmpDir;
+
+    // under test
+    private IntegrityFileManager mIntegrityFileManager;
+
+    @Before
+    public void setUp() throws Exception {
+        mTmpDir = Files.createTempDirectory("IntegrityFileManagerTest").toFile();
+        Slog.i(TAG, "Using temp directory " + mTmpDir);
+
+        // Use Xml Parser/Serializer to help with debugging since we can just print the file.
+        mIntegrityFileManager =
+                new IntegrityFileManager(
+                        new RuleXmlParser(), new RuleXmlSerializer(), mTmpDir);
+        Files.walk(mTmpDir.toPath())
+                .forEach(
+                        path -> {
+                            Slog.i(TAG, "before " + path);
+                        });
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Files.walk(mTmpDir.toPath())
+                .forEach(
+                        path -> {
+                            Slog.i(TAG, "after " + path);
+                        });
+        // Sorting paths in reverse order guarantees that we delete inside files before deleting
+        // directory.
+        Files.walk(mTmpDir.toPath())
+                .sorted(Comparator.reverseOrder())
+                .map(Path::toFile)
+                .forEach(File::delete);
+    }
+
+    @Test
+    public void testGetMetadata() throws Exception {
+        assertNull(mIntegrityFileManager.readMetadata());
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+
+        assertNotNull(mIntegrityFileManager.readMetadata());
+        assertEquals(VERSION, mIntegrityFileManager.readMetadata().getVersion());
+        assertEquals(RULE_PROVIDER, mIntegrityFileManager.readMetadata().getRuleProvider());
+    }
+
+    @Test
+    public void testGetRules() throws Exception {
+        String packageName = "package";
+        String packageCert = "cert";
+        int version = 123;
+        Rule packageNameRule =
+                new Rule(
+                        new StringAtomicFormula(
+                                AtomicFormula.PACKAGE_NAME,
+                                packageName,
+                                /* isHashedValue= */ false),
+                        Rule.DENY);
+        Rule packageCertRule =
+                new Rule(
+                        new StringAtomicFormula(
+                                AtomicFormula.APP_CERTIFICATE,
+                                packageCert,
+                                /* isHashedValue= */ false),
+                        Rule.DENY);
+        Rule versionCodeRule =
+                new Rule(
+                        new IntAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.LE, version),
+                        Rule.DENY);
+        Rule randomRule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.OR,
+                                Arrays.asList(
+                                        new StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                "abc",
+                                                /* isHashedValue= */ false),
+                                        new IntAtomicFormula(
+                                                AtomicFormula.VERSION_CODE,
+                                                AtomicFormula.LE,
+                                                version))),
+                        Rule.DENY);
+        // We will test the specifics of indexing in other classes. Here, we just require that all
+        // rules that are related to the given AppInstallMetadata are returned and do not assert
+        // anything on other rules.
+        List<Rule> rules =
+                Arrays.asList(packageNameRule, packageCertRule, versionCodeRule, randomRule);
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, rules);
+
+        AppInstallMetadata appInstallMetadata = new AppInstallMetadata.Builder()
+                .setPackageName(packageName)
+                .setAppCertificate(packageCert)
+                .setVersionCode(version)
+                .setInstallerName("abc")
+                .setInstallerCertificate("abc")
+                .setIsPreInstalled(true)
+                .build();
+        List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
+
+        assertThat(rulesFetched, hasItems(
+                equalTo(packageNameRule),
+                equalTo(packageCertRule),
+                equalTo(versionCodeRule)
+        ));
+    }
+
+    @Test
+    public void testIsInitialized() throws Exception {
+        assertFalse(mIntegrityFileManager.initialized());
+        mIntegrityFileManager.writeRules(VERSION, RULE_PROVIDER, Collections.EMPTY_LIST);
+        assertTrue(mIntegrityFileManager.initialized());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
new file mode 100644
index 0000000..94f68a5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue;
+import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.integrity.IntegrityUtils;
+import com.android.server.integrity.model.BitInputStream;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+@RunWith(JUnit4.class)
+public class BinaryFileOperationsTest {
+
+    private static final String IS_NOT_HASHED = "0";
+    private static final String IS_HASHED = "1";
+    private static final String PACKAGE_NAME = "com.test.app";
+    private static final String APP_CERTIFICATE = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+
+    @Test
+    public void testGetStringValue() throws IOException {
+        byte[] stringBytes =
+                getBytes(
+                        IS_NOT_HASHED
+                                + getBits(PACKAGE_NAME.length(), VALUE_SIZE_BITS)
+                                + getValueBits(PACKAGE_NAME));
+        ByteBuffer rule = ByteBuffer.allocate(stringBytes.length);
+        rule.put(stringBytes);
+        BitInputStream inputStream = new BitInputStream(rule.array());
+
+        String resultString = getStringValue(inputStream);
+
+        assertThat(resultString).isEqualTo(PACKAGE_NAME);
+    }
+
+    @Test
+    public void testGetHashedStringValue() throws IOException {
+        byte[] ruleBytes =
+                getBytes(
+                        IS_HASHED
+                                + getBits(APP_CERTIFICATE.length(), VALUE_SIZE_BITS)
+                                + getValueBits(APP_CERTIFICATE));
+        ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+        rule.put(ruleBytes);
+        BitInputStream inputStream = new BitInputStream(rule.array());
+
+        String resultString = getStringValue(inputStream);
+
+        assertThat(resultString)
+                .isEqualTo(IntegrityUtils.getHexDigest(
+                        APP_CERTIFICATE.getBytes(StandardCharsets.UTF_8)));
+    }
+
+    @Test
+    public void testGetStringValue_withSizeAndHashingInfo() throws IOException {
+        byte[] ruleBytes = getBytes(getValueBits(PACKAGE_NAME));
+        ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+        rule.put(ruleBytes);
+        BitInputStream inputStream = new BitInputStream(rule.array());
+
+        String resultString = getStringValue(inputStream,
+                PACKAGE_NAME.length(), /* isHashedValue= */false);
+
+        assertThat(resultString).isEqualTo(PACKAGE_NAME);
+    }
+
+    @Test
+    public void testGetIntValue() throws IOException {
+        int randomValue = 15;
+        byte[] ruleBytes = getBytes(getBits(randomValue, /* numOfBits= */ 32));
+        ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+        rule.put(ruleBytes);
+        BitInputStream inputStream = new BitInputStream(rule.array());
+
+        assertThat(getIntValue(inputStream)).isEqualTo(randomValue);
+    }
+
+    @Test
+    public void testGetBooleanValue_true() throws IOException {
+        String booleanValue = "1";
+        byte[] ruleBytes = getBytes(booleanValue);
+        ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+        rule.put(ruleBytes);
+        BitInputStream inputStream = new BitInputStream(rule.array());
+
+        assertThat(getBooleanValue(inputStream)).isEqualTo(true);
+    }
+
+    @Test
+    public void testGetBooleanValue_false() throws IOException {
+        String booleanValue = "0";
+        byte[] ruleBytes = getBytes(booleanValue);
+        ByteBuffer rule = ByteBuffer.allocate(ruleBytes.length);
+        rule.put(ruleBytes);
+        BitInputStream inputStream = new BitInputStream(rule.array());
+
+        assertThat(getBooleanValue(inputStream)).isEqualTo(false);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java
new file mode 100644
index 0000000..5ecb8b5c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/ByteTrackedOutputStreamTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity.serializer;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.integrity.model.BitOutputStream;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayOutputStream;
+
+@RunWith(JUnit4.class)
+public class ByteTrackedOutputStreamTest {
+
+    @Test
+    public void testConstructorStartsWithZeroBytesWritten() {
+        ByteTrackedOutputStream byteTrackedOutputStream =
+                new ByteTrackedOutputStream(new ByteArrayOutputStream());
+
+        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(0);
+    }
+
+    @Test
+    public void testSuccessfulWriteAndValidateWrittenBytesCount_directFromByteArray()
+            throws Exception {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
+
+        byte[] outputContent = "This is going to be outputed for tests.".getBytes();
+        byteTrackedOutputStream.write(outputContent);
+
+        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(outputContent.length);
+        assertThat(outputStream.toByteArray().length).isEqualTo(outputContent.length);
+    }
+
+    @Test
+    public void testSuccessfulWriteAndValidateWrittenBytesCount_fromBitStream() throws Exception {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ByteTrackedOutputStream byteTrackedOutputStream = new ByteTrackedOutputStream(outputStream);
+
+        BitOutputStream bitOutputStream = new BitOutputStream();
+        bitOutputStream.setNext(/* numOfBits= */5, /* value= */1);
+        byteTrackedOutputStream.write(bitOutputStream.toByteArray());
+
+        // Even though we wrote 5 bits, this will complete to 1 byte.
+        assertThat(byteTrackedOutputStream.getWrittenBytesCount()).isEqualTo(1);
+
+        // Add a bit less than 2 bytes (10 bits).
+        bitOutputStream.clear();
+        bitOutputStream.setNext(/* numOfBits= */10, /* value= */1);
+        byteTrackedOutputStream.write(bitOutputStream.toByteArray());
+
+        assertThat(outputStream.toByteArray().length).isEqualTo(3);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
index 981db6a..eb6698b 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -27,6 +27,9 @@
 import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
 import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY;
+import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE;
+import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY;
 import static com.android.server.integrity.utils.TestUtils.getBits;
 import static com.android.server.integrity.utils.TestUtils.getBytes;
 import static com.android.server.integrity.utils.TestUtils.getValueBits;
@@ -94,6 +97,15 @@
     private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
             getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
 
+    private static final String SERIALIZED_START_INDEXING_KEY =
+            IS_NOT_HASHED
+                    + getBits(START_INDEXING_KEY.length(), VALUE_SIZE_BITS)
+                    + getValueBits(START_INDEXING_KEY);
+    private static final String SERIALIZED_END_INDEXING_KEY =
+            IS_NOT_HASHED
+                    + getBits(END_INDEXING_KEY.length(), VALUE_SIZE_BITS)
+                    + getValueBits(END_INDEXING_KEY);
+
     @Test
     public void testBinaryString_serializeNullRules() {
         RuleSerializer binarySerializer = new RuleBinarySerializer();
@@ -107,15 +119,34 @@
 
     @Test
     public void testBinaryString_emptyRules() throws Exception {
-        ByteArrayOutputStream expectedArrayOutputStream = new ByteArrayOutputStream();
-        expectedArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
-
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
         RuleSerializer binarySerializer = new RuleBinarySerializer();
-        binarySerializer.serialize(
-                Collections.emptyList(), /* formatVersion= */ Optional.empty(), outputStream);
 
-        assertThat(outputStream.toByteArray()).isEqualTo(expectedArrayOutputStream.toByteArray());
+        binarySerializer.serialize(
+                Collections.emptyList(),
+                /* formatVersion= */ Optional.empty(),
+                ruleOutputStream,
+                indexingOutputStream);
+
+        ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream();
+        expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        assertThat(ruleOutputStream.toByteArray())
+                .isEqualTo(expectedRuleOutputStream.toByteArray());
+
+        ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+        byte[] expectedIndexingBytes =
+                getBytes(
+                        SERIALIZED_START_INDEXING_KEY
+                                + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+                                + SERIALIZED_END_INDEXING_KEY
+                                + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */
+                                32));
+        expectedIndexingOutputStream.write(expectedIndexingBytes);
+        expectedIndexingOutputStream.write(expectedIndexingBytes);
+        expectedIndexingOutputStream.write(expectedIndexingBytes);
+        assertThat(indexingOutputStream.toByteArray())
+                .isEqualTo(expectedIndexingOutputStream.toByteArray());
     }
 
     @Test
@@ -131,8 +162,16 @@
                                                 packageName,
                                                 /* isHashedValue= */ false))),
                         Rule.DENY);
+
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
         RuleSerializer binarySerializer = new RuleBinarySerializer();
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        binarySerializer.serialize(
+                Collections.singletonList(rule),
+                /* formatVersion= */ Optional.empty(),
+                ruleOutputStream,
+                indexingOutputStream);
+
         String expectedBits =
                 START_BIT
                         + COMPOUND_FORMULA_START_BITS
@@ -146,18 +185,29 @@
                         + COMPOUND_FORMULA_END_BITS
                         + DENY
                         + END_BIT;
-        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
-        byteArrayOutputStream.write(getBytes(expectedBits));
-        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+        ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream();
+        expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        expectedRuleOutputStream.write(getBytes(expectedBits));
+        assertThat(ruleOutputStream.toByteArray())
+                .isEqualTo(expectedRuleOutputStream.toByteArray());
 
-        binarySerializer.serialize(
-                Collections.singletonList(rule),
-                /* formatVersion= */ Optional.empty(),
-                outputStream);
-
-        byte[] actualRules = outputStream.toByteArray();
-        assertThat(actualRules).isEqualTo(expectedRules);
+        ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+        String expectedIndexingBitsForIndexed =
+                SERIALIZED_START_INDEXING_KEY
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+                        + SERIALIZED_END_INDEXING_KEY
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
+        expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForIndexed));
+        String expectedIndexingBitsForUnindexed =
+                SERIALIZED_START_INDEXING_KEY
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32)
+                        + SERIALIZED_END_INDEXING_KEY
+                        + getBits(DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(
+                        expectedBits).length, /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(getBytes(expectedIndexingBitsForUnindexed));
+        assertThat(indexingOutputStream.toByteArray())
+                .isEqualTo(expectedIndexingOutputStream.toByteArray());
     }
 
     @Test
@@ -453,91 +503,113 @@
     }
 
     @Test
-    public void testBinaryString_serializeComplexCompoundFormula_indexingOrderValid()
-            throws Exception {
-        String packageNameA = "aaa";
-        String packageNameB = "bbb";
-        String packageNameC = "ccc";
-        String appCert1 = "cert1";
-        String appCert2 = "cert2";
-        String appCert3 = "cert3";
-        Rule installerRule =
-                new Rule(
-                        new CompoundFormula(
-                                CompoundFormula.AND,
-                                Arrays.asList(
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.INSTALLER_NAME,
-                                                SAMPLE_INSTALLER_NAME,
-                                                /* isHashedValue= */ false),
-                                        new AtomicFormula.StringAtomicFormula(
-                                                AtomicFormula.INSTALLER_CERTIFICATE,
-                                                SAMPLE_INSTALLER_CERT,
-                                                /* isHashedValue= */ false))),
-                        Rule.DENY);
+    public void testBinaryString_verifyManyRulesAreIndexedCorrectly() throws Exception {
+        int ruleCount = 225;
+        String packagePrefix = "package.name.";
+        String appCertificatePrefix = "app.cert.";
+        String installerNamePrefix = "installer.";
 
-        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        // Create the rule set with 225 package name based rules, 225 app certificate indexed rules,
+        // and 225 non-indexed rules..
         List<Rule> ruleList = new ArrayList();
-        ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(appCert3));
-        ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(appCert2));
-        ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(appCert1));
-        ruleList.add(getRuleWithPackageNameAndSampleInstallerName(packageNameB));
-        ruleList.add(getRuleWithPackageNameAndSampleInstallerName(packageNameC));
-        ruleList.add(getRuleWithPackageNameAndSampleInstallerName(packageNameA));
-        ruleList.add(installerRule);
-        byte[] actualRules =
-                binarySerializer.serialize(ruleList, /* formatVersion= */ Optional.empty());
+        for (int count = 0; count < ruleCount; count++) {
+            ruleList.add(getRuleWithPackageNameAndSampleInstallerName(
+                    String.format("%s%04d", packagePrefix, count)));
+        }
+        for (int count = 0; count < ruleCount; count++) {
+            ruleList.add(getRuleWithAppCertificateAndSampleInstallerName(
+                    String.format("%s%04d", appCertificatePrefix, count)));
+        }
+        for (int count = 0; count < ruleCount; count++) {
+            ruleList.add(getNonIndexedRuleWithInstallerName(
+                    String.format("%s%04d", installerNamePrefix, count)));
+        }
 
-        // Note that ordering is important here and the test verifies that the rules are written
-        // in this sorted order.
-        ByteArrayOutputStream expectedArrayOutputStream = new ByteArrayOutputStream();
-        expectedArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
-        expectedArrayOutputStream.write(
-                getBytes(
-                        getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
-                                packageNameA)));
-        expectedArrayOutputStream.write(
-                getBytes(
-                        getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
-                                packageNameB)));
-        expectedArrayOutputStream.write(
-                getBytes(
-                        getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
-                                packageNameC)));
-        expectedArrayOutputStream.write(
-                getBytes(
-                        getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
-                                appCert1)));
-        expectedArrayOutputStream.write(
-                getBytes(
-                        getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
-                                appCert2)));
-        expectedArrayOutputStream.write(
-                getBytes(
-                        getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
-                                appCert3)));
-        String expectedBitsForInstallerRule =
-                START_BIT
-                        + COMPOUND_FORMULA_START_BITS
-                        + AND
-                        + ATOMIC_FORMULA_START_BITS
-                        + INSTALLER_NAME
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(SAMPLE_INSTALLER_NAME.length(), VALUE_SIZE_BITS)
-                        + getValueBits(SAMPLE_INSTALLER_NAME)
-                        + ATOMIC_FORMULA_START_BITS
-                        + INSTALLER_CERTIFICATE
-                        + EQ
-                        + IS_NOT_HASHED
-                        + getBits(SAMPLE_INSTALLER_CERT.length(), VALUE_SIZE_BITS)
-                        + getValueBits(SAMPLE_INSTALLER_CERT)
-                        + COMPOUND_FORMULA_END_BITS
-                        + DENY
-                        + END_BIT;
-        expectedArrayOutputStream.write(getBytes(expectedBitsForInstallerRule));
+        // Serialize the rules.
+        ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream();
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        binarySerializer.serialize(
+                ruleList,
+                /* formatVersion= */ Optional.empty(),
+                ruleOutputStream,
+                indexingOutputStream);
 
-        assertThat(actualRules).isEqualTo(expectedArrayOutputStream.toByteArray());
+        // Verify the rules file and index files.
+        ByteArrayOutputStream expectedOrderedRuleOutputStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream();
+
+        expectedOrderedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length;
+
+        String expectedIndexingBytesForPackageNameIndexed =
+                SERIALIZED_START_INDEXING_KEY
+                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+        for (int count = 0; count < ruleCount; count++) {
+            String packageName = String.format("%s%04d", packagePrefix, count);
+            if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
+                expectedIndexingBytesForPackageNameIndexed +=
+                        IS_NOT_HASHED
+                                + getBits(packageName.length(), VALUE_SIZE_BITS)
+                                + getValueBits(packageName)
+                                + getBits(totalBytesWritten, /* numOfBits= */ 32);
+            }
+
+            byte[] bytesForPackage =
+                    getBytes(getSerializedCompoundRuleWithPackageNameAndSampleInstallerName(
+                            packageName));
+            expectedOrderedRuleOutputStream.write(bytesForPackage);
+            totalBytesWritten += bytesForPackage.length;
+        }
+        expectedIndexingBytesForPackageNameIndexed +=
+                SERIALIZED_END_INDEXING_KEY
+                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForPackageNameIndexed));
+
+        String expectedIndexingBytesForAppCertificateIndexed =
+                SERIALIZED_START_INDEXING_KEY
+                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+        for (int count = 0; count < ruleCount; count++) {
+            String appCertificate = String.format("%s%04d", appCertificatePrefix, count);
+            if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) {
+                expectedIndexingBytesForAppCertificateIndexed +=
+                        IS_NOT_HASHED
+                                + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+                                + getValueBits(appCertificate)
+                                + getBits(totalBytesWritten, /* numOfBits= */ 32);
+            }
+
+            byte[] bytesForPackage =
+                    getBytes(getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName(
+                            appCertificate));
+            expectedOrderedRuleOutputStream.write(bytesForPackage);
+            totalBytesWritten += bytesForPackage.length;
+        }
+        expectedIndexingBytesForAppCertificateIndexed +=
+                SERIALIZED_END_INDEXING_KEY
+                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForAppCertificateIndexed));
+
+        String expectedIndexingBytesForUnindexed =
+                SERIALIZED_START_INDEXING_KEY
+                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+        for (int count = 0; count < ruleCount; count++) {
+            byte[] bytesForPackage =
+                    getBytes(getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
+                            String.format("%s%04d", installerNamePrefix, count)));
+            expectedOrderedRuleOutputStream.write(bytesForPackage);
+            totalBytesWritten += bytesForPackage.length;
+        }
+        expectedIndexingBytesForUnindexed +=
+                SERIALIZED_END_INDEXING_KEY
+                        + getBits(totalBytesWritten, /* numOfBits= */ 32);
+        expectedIndexingOutputStream.write(getBytes(expectedIndexingBytesForUnindexed));
+
+
+        assertThat(ruleOutputStream.toByteArray())
+                .isEqualTo(expectedOrderedRuleOutputStream.toByteArray());
+        assertThat(indexingOutputStream.toByteArray())
+                .isEqualTo(expectedIndexingOutputStream.toByteArray());
     }
 
     private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) {
@@ -616,6 +688,44 @@
                 + END_BIT;
     }
 
+    private Rule getNonIndexedRuleWithInstallerName(String installerName) {
+        return new Rule(
+                new CompoundFormula(
+                        CompoundFormula.AND,
+                        Arrays.asList(
+                                new AtomicFormula.StringAtomicFormula(
+                                        AtomicFormula.INSTALLER_NAME,
+                                        installerName,
+                                        /* isHashedValue= */ false),
+                                new AtomicFormula.StringAtomicFormula(
+                                        AtomicFormula.INSTALLER_CERTIFICATE,
+                                        SAMPLE_INSTALLER_CERT,
+                                        /* isHashedValue= */ false))),
+                Rule.DENY);
+    }
+
+    private String getSerializedCompoundRuleWithInstallerNameAndInstallerCert(
+            String installerName) {
+        return START_BIT
+                + COMPOUND_FORMULA_START_BITS
+                + AND
+                + ATOMIC_FORMULA_START_BITS
+                + INSTALLER_NAME
+                + EQ
+                + IS_NOT_HASHED
+                + getBits(installerName.length(), VALUE_SIZE_BITS)
+                + getValueBits(installerName)
+                + ATOMIC_FORMULA_START_BITS
+                + INSTALLER_CERTIFICATE
+                + EQ
+                + IS_NOT_HASHED
+                + getBits(SAMPLE_INSTALLER_CERT.length(), VALUE_SIZE_BITS)
+                + getValueBits(SAMPLE_INSTALLER_CERT)
+                + COMPOUND_FORMULA_END_BITS
+                + DENY
+                + END_BIT;
+    }
+
     private static Formula getInvalidFormula() {
         return new Formula() {
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
index 94e11c6..55fada4 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
@@ -38,8 +38,10 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 
 /** Unit tests for {@link RuleIndexingDetailsIdentifier}. */
 @RunWith(JUnit4.class)
@@ -138,14 +140,16 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_PACKAGE_NAME);
 
-        Map<Integer, List<Rule>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         // Verify the resulting map content.
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
         assertThat(result.get(NOT_INDEXED)).isEmpty();
         assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
-        assertThat(result.get(PACKAGE_NAME_INDEXED)).containsExactly(RULE_WITH_PACKAGE_NAME);
+        assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly(SAMPLE_PACKAGE_NAME);
+        assertThat(result.get(PACKAGE_NAME_INDEXED).get(SAMPLE_PACKAGE_NAME))
+                .containsExactly(RULE_WITH_PACKAGE_NAME);
     }
 
     @Test
@@ -153,13 +157,16 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_APP_CERTIFICATE);
 
-        Map<Integer, List<Rule>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
         assertThat(result.get(NOT_INDEXED)).isEmpty();
         assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
-        assertThat(result.get(APP_CERTIFICATE_INDEXED)).containsExactly(RULE_WITH_APP_CERTIFICATE);
+        assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet())
+                .containsExactly(SAMPLE_APP_CERTIFICATE);
+        assertThat(result.get(APP_CERTIFICATE_INDEXED).get(SAMPLE_APP_CERTIFICATE))
+                .containsExactly(RULE_WITH_APP_CERTIFICATE);
     }
 
     @Test
@@ -167,13 +174,14 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
 
-        Map<Integer, List<Rule>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
         assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
         assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
-        assertThat(result.get(NOT_INDEXED)).containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS);
+        assertThat(result.get(NOT_INDEXED).get("N/A"))
+                .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS);
     }
 
     @Test
@@ -181,13 +189,14 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
 
-        Map<Integer, List<Rule>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
         assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
         assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
-        assertThat(result.get(NOT_INDEXED)).containsExactly(RULE_WITH_NONSTRING_RESTRICTIONS);
+        assertThat(result.get(NOT_INDEXED).get("N/A"))
+                .containsExactly(RULE_WITH_NONSTRING_RESTRICTIONS);
     }
 
     @Test
@@ -206,13 +215,13 @@
         List<Rule> ruleList = new ArrayList();
         ruleList.add(negatedRule);
 
-        Map<Integer, List<Rule>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
         assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty();
         assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty();
-        assertThat(result.get(NOT_INDEXED)).containsExactly(negatedRule);
+        assertThat(result.get(NOT_INDEXED).get("N/A")).containsExactly(negatedRule);
     }
 
     @Test
@@ -234,22 +243,36 @@
         ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS);
         ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS);
 
-        Map<Integer, List<Rule>> result = splitRulesIntoIndexBuckets(ruleList);
+        Map<Integer, TreeMap<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList);
 
         assertThat(result.keySet())
                 .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED);
 
         // We check asserts this way to ensure ordering based on package name.
-        assertThat(result.get(PACKAGE_NAME_INDEXED).get(0)).isEqualTo(packageNameRuleA);
-        assertThat(result.get(PACKAGE_NAME_INDEXED).get(1)).isEqualTo(packageNameRuleB);
-        assertThat(result.get(PACKAGE_NAME_INDEXED).get(2)).isEqualTo(packageNameRuleC);
+        assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly("aaa", "bbb", "ccc");
+        Iterator<String> keySetIterator = result.get(PACKAGE_NAME_INDEXED).keySet().iterator();
+        assertThat(keySetIterator.next()).isEqualTo("aaa");
+        assertThat(keySetIterator.next()).isEqualTo("bbb");
+        assertThat(keySetIterator.next()).isEqualTo("ccc");
+        assertThat(result.get(PACKAGE_NAME_INDEXED).get("aaa")).containsExactly(packageNameRuleA);
+        assertThat(result.get(PACKAGE_NAME_INDEXED).get("bbb")).containsExactly(packageNameRuleB);
+        assertThat(result.get(PACKAGE_NAME_INDEXED).get("ccc")).containsExactly(packageNameRuleC);
 
         // We check asserts this way to ensure ordering based on app certificate.
-        assertThat(result.get(APP_CERTIFICATE_INDEXED).get(0)).isEqualTo(certificateRule1);
-        assertThat(result.get(APP_CERTIFICATE_INDEXED).get(1)).isEqualTo(certificateRule2);
-        assertThat(result.get(APP_CERTIFICATE_INDEXED).get(2)).isEqualTo(certificateRule3);
+        assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()).containsExactly("cert1", "cert2",
+                "cert3");
+        keySetIterator = result.get(APP_CERTIFICATE_INDEXED).keySet().iterator();
+        assertThat(keySetIterator.next()).isEqualTo("cert1");
+        assertThat(keySetIterator.next()).isEqualTo("cert2");
+        assertThat(keySetIterator.next()).isEqualTo("cert3");
+        assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert1")).containsExactly(
+                certificateRule1);
+        assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert2")).containsExactly(
+                certificateRule2);
+        assertThat(result.get(APP_CERTIFICATE_INDEXED).get("cert3")).containsExactly(
+                certificateRule3);
 
-        assertThat(result.get(NOT_INDEXED))
+        assertThat(result.get(NOT_INDEXED).get("N/A"))
                 .containsExactly(
                         RULE_WITH_INSTALLER_RESTRICTIONS,
                         RULE_WITH_NONSTRING_RESTRICTIONS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
index 0bb2d44..ff7722c 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
@@ -145,7 +145,8 @@
         xmlSerializer.serialize(
                 Collections.singletonList(rule),
                 /* formatVersion= */ Optional.empty(),
-                outputStream);
+                outputStream,
+                new ByteArrayOutputStream());
 
         byte[] actualRules = outputStream.toString().getBytes(StandardCharsets.UTF_8);
         assertEquals(expectedRules, new String(actualRules, StandardCharsets.UTF_8));
diff --git a/core/java/android/service/controls/templates/ThermostatTemplate.aidl b/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
similarity index 60%
copy from core/java/android/service/controls/templates/ThermostatTemplate.aidl
copy to services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
index 1fefda4..e159012 100644
--- a/core/java/android/service/controls/templates/ThermostatTemplate.aidl
+++ b/services/tests/servicestests/src/com/android/server/location/CustomCountryDetectorTestClass.java
@@ -14,6 +14,23 @@
  * limitations under the License.
  */
 
-package android.service.controls.templates;
+package com.android.server.location;
 
-parcelable ThermostatTemplate;
\ No newline at end of file
+import android.content.Context;
+import android.location.Country;
+
+public class CustomCountryDetectorTestClass extends CountryDetectorBase {
+    public CustomCountryDetectorTestClass(Context ctx) {
+        super(ctx);
+    }
+
+    @Override
+    public Country detectCountry() {
+        return null;
+    }
+
+    @Override
+    public void stop() {
+
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index ed70fa6..a3d15dd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -53,7 +53,6 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.widget.ILockSettings;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
 import com.android.internal.widget.LockscreenCredential;
@@ -91,7 +90,6 @@
     MockLockSettingsContext mContext;
     LockSettingsStorageTestable mStorage;
 
-    LockPatternUtils mLockPatternUtils;
     FakeGateKeeperService mGateKeeperService;
     NotificationManager mNotificationManager;
     UserManager mUserManager;
@@ -111,7 +109,6 @@
     FingerprintManager mFingerprintManager;
     FaceManager mFaceManager;
     PackageManager mPackageManager;
-    protected boolean mHasSecureLockScreen;
     FakeSettings mSettings;
 
     @Before
@@ -153,25 +150,14 @@
             storageDir.mkdirs();
         }
 
-        mHasSecureLockScreen = true;
-        mLockPatternUtils = new LockPatternUtils(mContext) {
-            @Override
-            public ILockSettings getLockSettings() {
-                return mService;
-            }
-
-            @Override
-            public boolean hasSecureLockScreen() {
-                return mHasSecureLockScreen;
-            }
-        };
         mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
                 mUserManager, mPasswordSlotManager);
         mAuthSecretService = mock(IAuthSecret.class);
-        mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
+        mService = new LockSettingsServiceTestable(mContext, mStorage,
                 mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
                 mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager,
                 mUserManagerInternal, mDeviceStateCache, mSettings);
+        mService.mHasSecureLockScreen = true;
         when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
         mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
         installChildProfile(MANAGED_PROFILE_USER_ID);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 7e7e170..271e8e2 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -32,7 +32,6 @@
 import android.security.KeyStore;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 
-import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 import com.android.server.ServiceThread;
 import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
@@ -46,7 +45,6 @@
         private LockSettingsStorage mLockSettingsStorage;
         private KeyStore mKeyStore;
         private IActivityManager mActivityManager;
-        private LockPatternUtils mLockPatternUtils;
         private IStorageManager mStorageManager;
         private SyntheticPasswordManager mSpManager;
         private FakeGsiService mGsiService;
@@ -56,7 +54,7 @@
         private FakeSettings mSettings;
 
         public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
-                IActivityManager activityManager, LockPatternUtils lockPatternUtils,
+                IActivityManager activityManager,
                 IStorageManager storageManager, SyntheticPasswordManager spManager,
                 FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
                 UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache,
@@ -65,7 +63,6 @@
             mLockSettingsStorage = storage;
             mKeyStore = keyStore;
             mActivityManager = activityManager;
-            mLockPatternUtils = lockPatternUtils;
             mStorageManager = storageManager;
             mSpManager = spManager;
             mGsiService = gsiService;
@@ -101,10 +98,6 @@
         }
 
         @Override
-        public LockPatternUtils getLockPatternUtils() {
-            return mLockPatternUtils;
-        }
-        @Override
         public DeviceStateCache getDeviceStateCache() {
             return mDeviceStateCache;
         }
@@ -158,14 +151,14 @@
 
     public MockInjector mInjector;
 
-    protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
+    protected LockSettingsServiceTestable(Context context,
             LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
             IStorageManager storageManager, IActivityManager mActivityManager,
             SyntheticPasswordManager spManager, IAuthSecret authSecretService,
             FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
             UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache,
             FakeSettings settings) {
-        super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
+        super(new MockInjector(context, storage, keystore, mActivityManager,
                 storageManager, spManager, gsiService,
                 recoverableKeyStoreManager, userManagerInternal, deviceStateCache, settings));
         mGateKeeperService = gatekeeper;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index abfda77..2e77c9f 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -423,7 +423,7 @@
 
     private void testCreateCredentialFailsWithoutLockScreen(
             int userId, LockscreenCredential credential) throws RemoteException {
-        mHasSecureLockScreen = false;
+        mService.mHasSecureLockScreen = false;
 
         try {
             mService.setLockCredential(credential, null, userId);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
new file mode 100644
index 0000000..54c552b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowDataTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * atest FrameworksServicesTests:RebootEscrowDataTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowDataTest {
+    private static byte[] getTestSp() {
+        byte[] testSp = new byte[10];
+        for (int i = 0; i < testSp.length; i++) {
+            testSp[i] = (byte) i;
+        }
+        return testSp;
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void fromEntries_failsOnNull() throws Exception {
+        RebootEscrowData.fromSyntheticPassword((byte) 2, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void fromEncryptedData_failsOnNullData() throws Exception {
+        byte[] testSp = getTestSp();
+        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+        SecretKeySpec key = RebootEscrowData.fromKeyBytes(expected.getKey());
+        RebootEscrowData.fromEncryptedData(key, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void fromEncryptedData_failsOnNullKey() throws Exception {
+        byte[] testSp = getTestSp();
+        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+        RebootEscrowData.fromEncryptedData(null, expected.getBlob());
+    }
+
+    @Test
+    public void fromEntries_loopback_success() throws Exception {
+        byte[] testSp = getTestSp();
+        RebootEscrowData expected = RebootEscrowData.fromSyntheticPassword((byte) 2, testSp);
+
+        SecretKeySpec key = RebootEscrowData.fromKeyBytes(expected.getKey());
+        RebootEscrowData actual = RebootEscrowData.fromEncryptedData(key, expected.getBlob());
+
+        assertThat(actual.getSpVersion(), is(expected.getSpVersion()));
+        assertThat(actual.getIv(), is(expected.getIv()));
+        assertThat(actual.getKey(), is(expected.getKey()));
+        assertThat(actual.getBlob(), is(expected.getBlob()));
+        assertThat(actual.getSyntheticPassword(), is(expected.getSyntheticPassword()));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
new file mode 100644
index 0000000..78a5a0b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_PRIMARY;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.rebootescrow.IRebootEscrow;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.widget.RebootEscrowListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.ArrayList;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowManagerTests {
+    protected static final int PRIMARY_USER_ID = 0;
+    protected static final int NONSECURE_USER_ID = 10;
+    private static final byte FAKE_SP_VERSION = 1;
+    private static final byte[] FAKE_AUTH_TOKEN = new byte[] {
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+            0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+    };
+
+    private Context mContext;
+    private UserManager mUserManager;
+    private RebootEscrowManager.Callbacks mCallbacks;
+    private IRebootEscrow mRebootEscrow;
+
+    LockSettingsStorageTestable mStorage;
+
+    private RebootEscrowManager mService;
+
+    static class MockInjector extends RebootEscrowManager.Injector {
+        private final IRebootEscrow mRebootEscrow;
+        private final UserManager mUserManager;
+
+        MockInjector(Context context, UserManager userManager, IRebootEscrow rebootEscrow) {
+            super(context);
+            mRebootEscrow = rebootEscrow;
+            mUserManager = userManager;
+        }
+
+        @Override
+        public UserManager getUserManager() {
+            return mUserManager;
+        }
+
+        @Override
+        public IRebootEscrow getRebootEscrow() {
+            return mRebootEscrow;
+        }
+    }
+
+    @Before
+    public void setUp_baseServices() throws Exception {
+        mContext = mock(Context.class);
+        mUserManager = mock(UserManager.class);
+        mCallbacks = mock(RebootEscrowManager.Callbacks.class);
+        mRebootEscrow = mock(IRebootEscrow.class);
+
+        mStorage = new LockSettingsStorageTestable(mContext,
+                new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+
+        ArrayList<UserInfo> users = new ArrayList<>();
+        users.add(new UserInfo(PRIMARY_USER_ID, "primary", FLAG_PRIMARY));
+        users.add(new UserInfo(NONSECURE_USER_ID, "non-secure", FLAG_FULL));
+        when(mUserManager.getUsers()).thenReturn(users);
+        when(mCallbacks.isUserSecure(PRIMARY_USER_ID)).thenReturn(true);
+        when(mCallbacks.isUserSecure(NONSECURE_USER_ID)).thenReturn(false);
+        mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow),
+                mCallbacks, mStorage);
+    }
+
+    @Test
+    public void prepareRebootEscrow_Success() throws Exception {
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mRebootEscrow);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mRebootEscrow, never()).storeKey(any());
+    }
+
+    @Test
+    public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+
+        clearInvocations(mRebootEscrow);
+        mService.clearRebootEscrow();
+        verify(mockListener).onPreparedForReboot(eq(false));
+        verify(mRebootEscrow).storeKey(eq(new byte[32]));
+    }
+
+    @Test
+    public void armService_Success() throws Exception {
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mRebootEscrow);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mRebootEscrow, never()).storeKey(any());
+
+        assertTrue(mService.armRebootEscrowIfNeeded());
+        verify(mRebootEscrow).storeKey(any());
+    }
+
+    @Test
+    public void armService_NoInitialization_Failure() throws Exception {
+        assertFalse(mService.armRebootEscrowIfNeeded());
+        verifyNoMoreInteractions(mRebootEscrow);
+    }
+
+    @Test
+    public void armService_RebootEscrowServiceException_Failure() throws Exception {
+        doThrow(RemoteException.class).when(mRebootEscrow).storeKey(any());
+        assertFalse(mService.armRebootEscrowIfNeeded());
+        verifyNoMoreInteractions(mRebootEscrow);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 7457067..8982925 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -381,7 +381,7 @@
         LockscreenCredential pattern = newPattern("123654");
         byte[] token = "some-high-entropy-secure-token".getBytes();
 
-        mHasSecureLockScreen = false;
+        mService.mHasSecureLockScreen = false;
         enableSyntheticPassword();
         long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null);
         assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID));
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 7529bc5..28e6830 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -1138,11 +1138,11 @@
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
-            expectMobileDefaults();
+            TelephonyManager tmSub = expectMobileDefaults();
 
             mService.updateNetworks();
 
-            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
             verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
                     DataUnit.MEGABYTES.toBytes(1800 - 360));
             verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
@@ -1155,11 +1155,11 @@
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
-            expectMobileDefaults();
+            TelephonyManager tmSub = expectMobileDefaults();
 
             mService.updateNetworks();
 
-            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
             verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
                     DataUnit.MEGABYTES.toBytes(1800 - 1799));
             verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_WARNING),
@@ -1173,12 +1173,12 @@
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
-            expectMobileDefaults();
+            TelephonyManager tmSub = expectMobileDefaults();
             expectDefaultCarrierConfig();
 
             mService.updateNetworks();
 
-            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
             verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
                     DataUnit.MEGABYTES.toBytes(1800 - 1799));
             // Since this isn't from the identified carrier, there should be no notifications
@@ -1192,11 +1192,11 @@
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
-            expectMobileDefaults();
+            TelephonyManager tmSub = expectMobileDefaults();
 
             mService.updateNetworks();
 
-            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(false, TEST_SUB_ID);
+            verify(tmSub, atLeastOnce()).setPolicyDataEnabled(false);
             verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, 1);
             verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT),
                     isA(Notification.class), eq(UserHandle.ALL));
@@ -1205,12 +1205,12 @@
         // Snooze limit
         {
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
-            expectMobileDefaults();
+            TelephonyManager tmSub = expectMobileDefaults();
 
             mService.snoozeLimit(NetworkTemplate.buildTemplateMobileAll(TEST_IMSI));
             mService.updateNetworks();
 
-            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(tmSub, atLeastOnce()).setPolicyDataEnabled(true);
             verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
                     Long.MAX_VALUE);
             verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT_SNOOZED),
@@ -1928,10 +1928,11 @@
                 .thenReturn(CarrierConfigManager.getDefaultConfig());
     }
 
-    private void expectMobileDefaults() throws Exception {
-        setupTelephonySubscriptionManagers(TEST_SUB_ID, TEST_IMSI);
-        doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
+    private TelephonyManager expectMobileDefaults() throws Exception {
+        TelephonyManager tmSub = setupTelephonySubscriptionManagers(TEST_SUB_ID, TEST_IMSI);
+        doNothing().when(tmSub).setPolicyDataEnabled(anyBoolean());
         expectNetworkState(false /* roaming */);
+        return tmSub;
     }
 
     private void verifyAdvisePersistThreshold() throws Exception {
@@ -2090,8 +2091,10 @@
 
     /**
      * Creates a mock {@link TelephonyManager} and {@link SubscriptionManager}.
+     *
      */
-    private void setupTelephonySubscriptionManagers(int subscriptionId, String subscriberId) {
+    private TelephonyManager setupTelephonySubscriptionManagers(int subscriptionId,
+            String subscriberId) {
         when(mSubscriptionManager.getActiveSubscriptionInfoList()).thenReturn(
                 createSubscriptionInfoList(subscriptionId));
 
@@ -2101,6 +2104,7 @@
         when(subTelephonyManager.getSubscriberId()).thenReturn(subscriberId);
         when(mTelephonyManager.createForSubscriptionId(subscriptionId))
                 .thenReturn(subTelephonyManager);
+        return subTelephonyManager;
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
new file mode 100644
index 0000000..d3166b9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.people;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionSessionId;
+import android.app.prediction.AppTarget;
+import android.app.prediction.IPredictionCallback;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.os.Binder;
+import android.os.RemoteException;
+
+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.List;
+import java.util.function.Consumer;
+
+@RunWith(JUnit4.class)
+public final class PeopleServiceTest {
+
+    private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
+    private static final int APP_PREDICTION_TARGET_COUNT = 4;
+    private static final String TEST_PACKAGE_NAME = "com.example";
+
+    private PeopleServiceInternal mServiceInternal;
+    private PeopleService.LocalService mLocalService;
+    private AppPredictionSessionId mSessionId;
+    private AppPredictionContext mPredictionContext;
+
+    @Mock private Context mContext;
+    @Mock private IPredictionCallback mCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(mCallback.asBinder()).thenReturn(new Binder());
+
+        PeopleService service = new PeopleService(mContext);
+        service.onStart();
+
+        mServiceInternal = LocalServices.getService(PeopleServiceInternal.class);
+        mLocalService = (PeopleService.LocalService) mServiceInternal;
+
+        mSessionId = new AppPredictionSessionId("abc");
+        mPredictionContext = new AppPredictionContext.Builder(mContext)
+                .setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
+                .setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
+                .build();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(PeopleServiceInternal.class);
+    }
+
+    @Test
+    public void testRegisterCallbacks() throws RemoteException {
+        mServiceInternal.onCreatePredictionSession(mPredictionContext, mSessionId);
+
+        SessionInfo sessionInfo = mLocalService.getSessionInfo(mSessionId);
+
+        mServiceInternal.registerPredictionUpdates(mSessionId, mCallback);
+
+        Consumer<List<AppTarget>> updatePredictionMethod =
+                sessionInfo.getPredictor().getUpdatePredictionsMethod();
+        updatePredictionMethod.accept(new ArrayList<>());
+        updatePredictionMethod.accept(new ArrayList<>());
+
+        verify(mCallback, times(2)).onResult(any(ParceledListSlice.class));
+
+        mServiceInternal.unregisterPredictionUpdates(mSessionId, mCallback);
+
+        updatePredictionMethod.accept(new ArrayList<>());
+
+        // After the un-registration, the callback should no longer be called.
+        verify(mCallback, times(2)).onResult(any(ParceledListSlice.class));
+
+        mServiceInternal.onDestroyPredictionSession(mSessionId);
+    }
+}
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 f9fc3a1..41416f1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -88,7 +88,6 @@
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
@@ -114,6 +113,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
@@ -1241,7 +1241,7 @@
     protected void setCaller(String packageName, int userId) {
         mInjectedClientPackage = packageName;
         mInjectedCallingUid =
-                Preconditions.checkNotNull(getInjectedPackageInfo(packageName, userId, false),
+                Objects.requireNonNull(getInjectedPackageInfo(packageName, userId, false),
                         "Unknown package").applicationInfo.uid;
 
         // Set up LauncherApps for this caller.
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 1e760cc..ac27a08 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -139,18 +139,18 @@
         assertEquals("A Name", mUserManagerService.getUserInfo(TEST_ID).name);
     }
 
-    /** Test UMS.getUserTypeForUser(). */
+    /** Test UMS.isUserOfType(). */
     @Test
-    public void testGetUserTypeForUser() throws Exception {
-        final String typeSys = mUserManagerService.getUserTypeForUser(UserHandle.USER_SYSTEM);
-        assertTrue("System user was of invalid type " + typeSys,
-                typeSys.equals(USER_TYPE_SYSTEM_HEADLESS) || typeSys.equals(USER_TYPE_FULL_SYSTEM));
+    public void testIsUserOfType() throws Exception {
+        assertTrue("System user was of invalid type",
+                mUserManagerService.isUserOfType(UserHandle.USER_SYSTEM, USER_TYPE_SYSTEM_HEADLESS)
+                || mUserManagerService.isUserOfType(UserHandle.USER_SYSTEM, USER_TYPE_FULL_SYSTEM));
 
         final int testId = 100;
         final String typeName = "A type";
         UserInfo userInfo = createUser(testId, 0, typeName);
         mUserManagerService.putUserInfo(userInfo);
-        assertEquals(typeName, mUserManagerService.getUserTypeForUser(testId));
+        assertTrue(mUserManagerService.isUserOfType(testId, typeName));
     }
 
     /** Tests upgradeIfNecessaryLP (but without locking) for upgrading from version 8 to 9+. */
@@ -169,22 +169,22 @@
 
         mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1);
 
-        assertEquals(USER_TYPE_PROFILE_MANAGED, mUserManagerService.getUserTypeForUser(100));
+        assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED));
         assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0);
 
-        assertEquals(USER_TYPE_FULL_GUEST, mUserManagerService.getUserTypeForUser(101));
+        assertTrue(mUserManagerService.isUserOfType(101, USER_TYPE_FULL_GUEST));
 
-        assertEquals(USER_TYPE_FULL_RESTRICTED, mUserManagerService.getUserTypeForUser(102));
+        assertTrue(mUserManagerService.isUserOfType(102, USER_TYPE_FULL_RESTRICTED));
         assertTrue((mUserManagerService.getUserInfo(102).flags & FLAG_PROFILE) == 0);
 
-        assertEquals(USER_TYPE_FULL_SECONDARY, mUserManagerService.getUserTypeForUser(103));
+        assertTrue(mUserManagerService.isUserOfType(103, USER_TYPE_FULL_SECONDARY));
         assertTrue((mUserManagerService.getUserInfo(103).flags & FLAG_PROFILE) == 0);
 
-        assertEquals(USER_TYPE_SYSTEM_HEADLESS, mUserManagerService.getUserTypeForUser(104));
+        assertTrue(mUserManagerService.isUserOfType(104, USER_TYPE_SYSTEM_HEADLESS));
 
-        assertEquals(USER_TYPE_FULL_SYSTEM, mUserManagerService.getUserTypeForUser(105));
+        assertTrue(mUserManagerService.isUserOfType(105, USER_TYPE_FULL_SYSTEM));
 
-        assertEquals(USER_TYPE_FULL_DEMO, mUserManagerService.getUserTypeForUser(106));
+        assertTrue(mUserManagerService.isUserOfType(106, USER_TYPE_FULL_DEMO));
     }
 
     /** Creates a UserInfo with the given flags and userType. */
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 06b3dc1..77376f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -21,6 +21,7 @@
 
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
+import static org.testng.Assert.assertThrows;
 
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
@@ -140,20 +141,15 @@
         assertThat(userInfo).isNotNull();
 
         List<UserInfo> list = mUserManager.getUsers();
-        boolean found = false;
         for (UserInfo user : list) {
             if (user.id == userInfo.id && user.name.equals("Guest 1")
                     && user.isGuest()
                     && !user.isAdmin()
                     && !user.isPrimary()) {
-                found = true;
-                Bundle restrictions = mUserManager.getUserRestrictions(user.getUserHandle());
-                assertWithMessage("Guest user should have DISALLOW_CONFIG_WIFI=true by default")
-                        .that(restrictions.getBoolean(UserManager.DISALLOW_CONFIG_WIFI))
-                        .isTrue();
+                return;
             }
         }
-        assertThat(found).isTrue();
+        fail("Didn't find a guest: " + list);
     }
 
     @MediumTest
@@ -206,14 +202,7 @@
     @MediumTest
     @Test
     public void testRemoveUserByHandle_ThrowsException() {
-        synchronized (mUserRemoveLock) {
-            try {
-                mUserManager.removeUser(null);
-                fail("Expected IllegalArgumentException on passing in a null UserHandle.");
-            } catch (IllegalArgumentException expected) {
-                // Do nothing - exception is expected.
-            }
-        }
+        assertThrows(IllegalArgumentException.class, () -> mUserManager.removeUser(null));
     }
 
     /** Tests creating a FULL user via specifying userType. */
@@ -343,7 +332,6 @@
                 UserManager.USER_TYPE_PROFILE_MANAGED, primaryUserId);
         assertThat(userInfo).isNotNull();
         final int userId = userInfo.id;
-        final UserHandle userHandle = new UserHandle(userId);
 
         assertThat(mUserManager.hasBadge(userId)).isEqualTo(userTypeDetails.hasBadge());
         assertThat(mUserManager.getUserIconBadgeResId(userId))
@@ -353,13 +341,13 @@
         assertThat(mUserManager.getUserBadgeNoBackgroundResId(userId))
                 .isEqualTo(userTypeDetails.getBadgeNoBackground());
         assertThat(mUserManager.isProfile(userId)).isEqualTo(userTypeDetails.isProfile());
-        assertThat(mUserManager.getUserTypeForUser(userHandle))
-                .isEqualTo(userTypeDetails.getName());
+        assertThat(mUserManager.isUserOfType(asHandle(userId), userTypeDetails.getName()))
+                .isTrue();
 
         final int badgeIndex = userInfo.profileBadge;
         assertThat(mUserManager.getUserBadgeColor(userId)).isEqualTo(
                 Resources.getSystem().getColor(userTypeDetails.getBadgeColor(badgeIndex), null));
-        assertThat(mUserManager.getBadgedLabelForUser("Test", userHandle)).isEqualTo(
+        assertThat(mUserManager.getBadgedLabelForUser("Test", asHandle(userId))).isEqualTo(
                 Resources.getSystem().getString(userTypeDetails.getBadgeLabel(badgeIndex), "Test"));
     }
 
@@ -438,9 +426,8 @@
     @MediumTest
     @Test
     public void testCreateUser_disallowAddUser() throws Exception {
-        final int creatorId = isAutomotive() ? ActivityManager.getCurrentUser()
-                : mUserManager.getPrimaryUser().id;
-        final UserHandle creatorHandle = new UserHandle(creatorId);
+        final int creatorId = ActivityManager.getCurrentUser();
+        final UserHandle creatorHandle = asHandle(creatorId);
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, creatorHandle);
         try {
             UserInfo createadInfo = createUser("SecondaryUser", /*flags=*/ 0);
@@ -457,7 +444,7 @@
     public void testCreateProfileForUser_disallowAddManagedProfile() throws Exception {
         assumeManagedUsersSupported();
         final int primaryUserId = mUserManager.getPrimaryUser().id;
-        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        final UserHandle primaryUserHandle = asHandle(primaryUserId);
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
                 primaryUserHandle);
         try {
@@ -476,7 +463,7 @@
     public void testCreateProfileForUserEvenWhenDisallowed() throws Exception {
         assumeManagedUsersSupported();
         final int primaryUserId = mUserManager.getPrimaryUser().id;
-        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        final UserHandle primaryUserHandle = asHandle(primaryUserId);
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_MANAGED_PROFILE, true,
                 primaryUserHandle);
         try {
@@ -495,7 +482,7 @@
     public void testCreateProfileForUser_disallowAddUser() throws Exception {
         assumeManagedUsersSupported();
         final int primaryUserId = mUserManager.getPrimaryUser().id;
-        final UserHandle primaryUserHandle = new UserHandle(primaryUserId);
+        final UserHandle primaryUserHandle = asHandle(primaryUserId);
         mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, primaryUserHandle);
         try {
             UserInfo userInfo = createProfileForUser("Managed",
@@ -540,8 +527,7 @@
 
     @MediumTest
     @Test
-    public void testGetUserCreationTime() throws Exception {
-        // TODO: should add a regular user instead of a profile, so it can be tested everywhere
+    public void testGetManagedProfileCreationTime() throws Exception {
         assumeManagedUsersSupported();
         final int primaryUserId = mUserManager.getPrimaryUser().id;
         final long startTime = System.currentTimeMillis();
@@ -556,38 +542,40 @@
             assertWithMessage("creationTime must be 0 if the time is not > EPOCH_PLUS_30_years")
                     .that(profile.creationTime).isEqualTo(0);
         }
-        assertThat(mUserManager.getUserCreationTime(
-                new UserHandle(profile.id))).isEqualTo(profile.creationTime);
+        assertThat(mUserManager.getUserCreationTime(asHandle(profile.id)))
+                .isEqualTo(profile.creationTime);
 
         long ownerCreationTime = mUserManager.getUserInfo(primaryUserId).creationTime;
-        assertThat(mUserManager.getUserCreationTime(
-                new UserHandle(primaryUserId))).isEqualTo(ownerCreationTime);
+        assertThat(mUserManager.getUserCreationTime(asHandle(primaryUserId)))
+            .isEqualTo(ownerCreationTime);
+    }
+
+    @MediumTest
+    @Test
+    public void testGetUserCreationTime() throws Exception {
+        long startTime = System.currentTimeMillis();
+        UserInfo user = createUser("User", /* flags= */ 0);
+        long endTime = System.currentTimeMillis();
+        assertThat(user).isNotNull();
+        assertWithMessage("creationTime must be set when the user is created")
+            .that(user.creationTime).isIn(Range.closed(startTime, endTime));
     }
 
     @SmallTest
     @Test
     public void testGetUserCreationTime_nonExistentUser() throws Exception {
-        try {
-            int noSuchUserId = 100500;
-            mUserManager.getUserCreationTime(new UserHandle(noSuchUserId));
-            fail("SecurityException should be thrown for nonexistent user");
-        } catch (Exception e) {
-            assertWithMessage("SecurityException should be thrown for nonexistent user").that(e)
-                    .isInstanceOf(SecurityException.class);
-        }
+        int noSuchUserId = 100500;
+        assertThrows(SecurityException.class,
+                () -> mUserManager.getUserCreationTime(asHandle(noSuchUserId)));
     }
 
     @SmallTest
     @Test
     public void testGetUserCreationTime_otherUser() throws Exception {
         UserInfo user = createUser("User 1", 0);
-        try {
-            mUserManager.getUserCreationTime(new UserHandle(user.id));
-            fail("SecurityException should be thrown for other user");
-        } catch (Exception e) {
-            assertWithMessage("SecurityException should be thrown for other user").that(e)
-                    .isInstanceOf(SecurityException.class);
-        }
+        assertThat(user).isNotNull();
+        assertThrows(SecurityException.class,
+                () -> mUserManager.getUserCreationTime(asHandle(user.id)));
     }
 
     private boolean findUser(int id) {
@@ -658,11 +646,11 @@
         UserInfo testUser = createUser("User 1", 0);
 
         mUserManager.setUserRestriction(
-                UserManager.DISALLOW_INSTALL_APPS, true, new UserHandle(testUser.id));
+                UserManager.DISALLOW_INSTALL_APPS, true, asHandle(testUser.id));
         mUserManager.setUserRestriction(
-                UserManager.DISALLOW_CONFIG_WIFI, false, new UserHandle(testUser.id));
+                UserManager.DISALLOW_CONFIG_WIFI, false, asHandle(testUser.id));
 
-        Bundle stored = mUserManager.getUserRestrictions(new UserHandle(testUser.id));
+        Bundle stored = mUserManager.getUserRestrictions(asHandle(testUser.id));
         // Note this will fail if DO already sets those restrictions.
         assertThat(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI)).isFalse();
         assertThat(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS)).isFalse();
@@ -738,15 +726,8 @@
 
     @Test
     public void testSwitchUserByHandle_ThrowsException() {
-        synchronized (mUserSwitchLock) {
-            try {
-                ActivityManager am = mContext.getSystemService(ActivityManager.class);
-                am.switchUser(null);
-                fail("Expected IllegalArgumentException on passing in a null UserHandle.");
-            } catch (IllegalArgumentException expected) {
-                // Do nothing - exception is expected.
-            }
-        }
+        ActivityManager am = mContext.getSystemService(ActivityManager.class);
+        assertThrows(IllegalArgumentException.class, () -> am.switchUser(null));
     }
 
     @MediumTest
@@ -903,4 +884,8 @@
     private boolean isAutomotive() {
         return mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
+
+    private static UserHandle asHandle(int userId) {
+        return new UserHandle(userId);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 1f312bf..d5cdbeb 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -20,16 +20,19 @@
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doThrow;
 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.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.IntentSender;
 import android.os.Handler;
 import android.os.IPowerManager;
 import android.os.IRecoverySystemProgressListener;
@@ -40,6 +43,8 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.widget.LockSettingsInternal;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -58,6 +63,7 @@
     private Context mContext;
     private IPowerManager mIPowerManager;
     private FileWriter mUncryptUpdateFileWriter;
+    private LockSettingsInternal mLockSettingsInternal;
 
     @Before
     public void setup() {
@@ -65,6 +71,9 @@
         mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
         mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
         mUncryptUpdateFileWriter = mock(FileWriter.class);
+        mLockSettingsInternal = mock(LockSettingsInternal.class);
+
+        when(mLockSettingsInternal.armRebootEscrow()).thenReturn(true);
 
         Looper looper = InstrumentationRegistry.getContext().getMainLooper();
         mIPowerManager = mock(IPowerManager.class);
@@ -72,7 +81,7 @@
                 new Handler(looper));
 
         mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
-                powerManager, mUncryptUpdateFileWriter, mUncryptSocket);
+                powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
     }
 
     @Test
@@ -194,4 +203,100 @@
         verify(mUncryptSocket).sendAck();
         verify(mUncryptSocket).close();
     }
+
+    @Test(expected = SecurityException.class)
+    public void requestLskf_protected() {
+        doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.RECOVERY), any());
+        mRecoverySystemService.requestLskf("test", null);
+    }
+
+
+    @Test
+    public void requestLskf_nullToken_failure() {
+        assertThat(mRecoverySystemService.requestLskf(null, null), is(false));
+    }
+
+    @Test
+    public void requestLskf_success() throws Exception {
+        IntentSender intentSender = mock(IntentSender.class);
+        assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+        mRecoverySystemService.onPreparedForReboot(true);
+        verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+    }
+
+    @Test
+    public void requestLskf_subsequentRequestClearsPrepared() throws Exception {
+        IntentSender intentSender = mock(IntentSender.class);
+        assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+        mRecoverySystemService.onPreparedForReboot(true);
+        verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+
+        assertThat(mRecoverySystemService.requestLskf("test2", null), is(true));
+        assertThat(mRecoverySystemService.rebootWithLskf("test", null), is(false));
+        assertThat(mRecoverySystemService.rebootWithLskf("test2", "foobar"), is(false));
+
+        mRecoverySystemService.onPreparedForReboot(true);
+        assertThat(mRecoverySystemService.rebootWithLskf("test2", "foobar"), is(true));
+        verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+        verify(mIPowerManager).reboot(anyBoolean(), eq("foobar"), anyBoolean());
+    }
+
+
+    @Test
+    public void requestLskf_requestedButNotPrepared() throws Exception {
+        IntentSender intentSender = mock(IntentSender.class);
+        assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+        verify(intentSender, never()).sendIntent(any(), anyInt(), any(), any(), any());
+    }
+
+    @Test(expected = SecurityException.class)
+    public void clearLskf_protected() {
+        doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.RECOVERY), any());
+        mRecoverySystemService.clearLskf();
+    }
+
+    @Test
+    public void clearLskf_requestedThenCleared() throws Exception {
+        IntentSender intentSender = mock(IntentSender.class);
+        assertThat(mRecoverySystemService.requestLskf("test", intentSender), is(true));
+        mRecoverySystemService.onPreparedForReboot(true);
+        verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
+
+        assertThat(mRecoverySystemService.clearLskf(), is(true));
+        verify(mLockSettingsInternal).clearRebootEscrow();
+    }
+
+    @Test
+    public void startup_setRebootEscrowListener() throws Exception {
+        mRecoverySystemService.onSystemServicesReady();
+        verify(mLockSettingsInternal).setRebootEscrowListener(any());
+    }
+
+    @Test(expected = SecurityException.class)
+    public void rebootWithLskf_protected() {
+        doThrow(SecurityException.class).when(mContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.RECOVERY), any());
+        mRecoverySystemService.rebootWithLskf("test1", null);
+    }
+
+    @Test
+    public void rebootWithLskf_Success() throws Exception {
+        assertThat(mRecoverySystemService.requestLskf("test", null), is(true));
+        mRecoverySystemService.onPreparedForReboot(true);
+        assertThat(mRecoverySystemService.rebootWithLskf("test", "ab-update"), is(true));
+        verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
+    }
+
+    @Test
+    public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
+        assertThat(mRecoverySystemService.rebootWithLskf("test1", null), is(false));
+    }
+
+    @Test
+    public void rebootWithLskf_withNullUpdateToken_Failure() throws Exception {
+        assertThat(mRecoverySystemService.rebootWithLskf(null, null), is(false));
+        verifyNoMoreInteractions(mIPowerManager);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
index a986b71..131e4f3 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -19,6 +19,8 @@
 import android.content.Context;
 import android.os.PowerManager;
 
+import com.android.internal.widget.LockSettingsInternal;
+
 import java.io.FileWriter;
 
 public class RecoverySystemServiceTestable extends RecoverySystemService {
@@ -27,15 +29,17 @@
         private final PowerManager mPowerManager;
         private final FileWriter mUncryptPackageFileWriter;
         private final UncryptSocket mUncryptSocket;
+        private final LockSettingsInternal mLockSettingsInternal;
 
         MockInjector(Context context, FakeSystemProperties systemProperties,
                 PowerManager powerManager, FileWriter uncryptPackageFileWriter,
-                UncryptSocket uncryptSocket) {
+                UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
             super(context);
             mSystemProperties = systemProperties;
             mPowerManager = powerManager;
             mUncryptPackageFileWriter = uncryptPackageFileWriter;
             mUncryptSocket = uncryptSocket;
+            mLockSettingsInternal = lockSettingsInternal;
         }
 
         @Override
@@ -76,13 +80,18 @@
         @Override
         public void threadSleep(long millis) {
         }
+
+        @Override
+        public LockSettingsInternal getLockSettingsService() {
+            return mLockSettingsInternal;
+        }
     }
 
     RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
             PowerManager powerManager, FileWriter uncryptPackageFileWriter,
-            UncryptSocket uncryptSocket) {
+            UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
         super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
-                uncryptSocket));
+                uncryptSocket, lockSettingsInternal));
     }
 
     public static class FakeSystemProperties {
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
index aec489c..757a884 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackStoreTest.java
@@ -53,8 +53,7 @@
                     if (a == null || b == null) {
                         return a == b;
                     }
-                    return a.getLongVersionCode() == b.getLongVersionCode()
-                            && Objects.equals(a.getPackageName(), b.getPackageName());
+                    return a.equals(b);
                 }
 
                 @Override
@@ -298,15 +297,8 @@
     private void assertPackageRollbacksAreEquivalent(PackageRollbackInfo b, PackageRollbackInfo a) {
         assertThat(b.getPackageName()).isEqualTo(a.getPackageName());
 
-        assertThat(b.getVersionRolledBackFrom().getLongVersionCode())
-                .isEqualTo(a.getVersionRolledBackFrom().getLongVersionCode());
-        assertThat(b.getVersionRolledBackFrom().getPackageName())
-                .isEqualTo(a.getVersionRolledBackFrom().getPackageName());
-
-        assertThat(b.getVersionRolledBackTo().getLongVersionCode())
-                .isEqualTo(a.getVersionRolledBackTo().getLongVersionCode());
-        assertThat(b.getVersionRolledBackTo().getPackageName())
-                .isEqualTo(a.getVersionRolledBackTo().getPackageName());
+        assertThat(b.getVersionRolledBackFrom()).isEqualTo(a.getVersionRolledBackFrom());
+        assertThat(b.getVersionRolledBackTo()).isEqualTo(a.getVersionRolledBackTo());
 
         assertThat(b.getPendingBackups().toArray()).isEqualTo(a.getPendingBackups().toArray());
 
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
index 82f32f8..f8915c0 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java
@@ -60,8 +60,6 @@
 import android.os.IHwInterface;
 import android.os.RemoteException;
 
-import androidx.test.runner.AndroidJUnit4;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -144,7 +142,11 @@
         properties.maxSoundModels = 456;
         properties.maxKeyPhrases = 567;
         properties.maxUsers = 678;
-        properties.recognitionModes = 789;
+        properties.recognitionModes =
+                android.hardware.soundtrigger.V2_0.RecognitionMode.VOICE_TRIGGER
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_IDENTIFICATION
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.USER_AUTHENTICATION
+                | android.hardware.soundtrigger.V2_0.RecognitionMode.GENERIC_TRIGGER;
         properties.captureTransition = true;
         properties.maxBufferMs = 321;
         properties.concurrentCapture = supportConcurrentCapture;
@@ -162,7 +164,10 @@
         assertEquals(456, properties.maxSoundModels);
         assertEquals(567, properties.maxKeyPhrases);
         assertEquals(678, properties.maxUsers);
-        assertEquals(789, properties.recognitionModes);
+        assertEquals(RecognitionMode.GENERIC_TRIGGER
+                | RecognitionMode.USER_AUTHENTICATION
+                | RecognitionMode.USER_IDENTIFICATION
+                | RecognitionMode.VOICE_TRIGGER, properties.recognitionModes);
         assertTrue(properties.captureTransition);
         assertEquals(321, properties.maxBufferMs);
         assertEquals(supportConcurrentCapture, properties.concurrentCapture);
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 72a7f50..71b568c 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -18,15 +18,18 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -77,6 +80,22 @@
         mHandlerThread.join();
     }
 
+    @Test(expected = SecurityException.class)
+    public void testSuggestPhoneTime_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingPermission(anyString(), any());
+        PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion();
+
+        try {
+            mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingPermission(
+                    eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
+                    anyString());
+        }
+    }
+
     @Test
     public void testSuggestPhoneTime() throws Exception {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
@@ -86,13 +105,29 @@
         mTestHandler.assertTotalMessagesEnqueued(1);
 
         verify(mMockContext).enforceCallingPermission(
-                eq(android.Manifest.permission.SET_TIME),
+                eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE),
                 anyString());
 
         mTestHandler.waitForEmptyQueue();
         mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testSuggestManualTime_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
+
+        try {
+            mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
+                    anyString());
+        }
+    }
+
     @Test
     public void testSuggestManualTime() throws Exception {
         doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
@@ -102,13 +137,43 @@
         mTestHandler.assertTotalMessagesEnqueued(1);
 
         verify(mMockContext).enforceCallingOrSelfPermission(
-                eq(android.Manifest.permission.SET_TIME),
+                eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
                 anyString());
 
         mTestHandler.waitForEmptyQueue();
         mStubbedTimeDetectorStrategy.verifySuggestManualTimeCalled(manualTimeSuggestion);
     }
 
+    @Test(expected = SecurityException.class)
+    public void testSuggestNetworkTime_withoutPermission() {
+        doThrow(new SecurityException("Mock"))
+                .when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+        NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion();
+
+        try {
+            mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion);
+            fail();
+        } finally {
+            verify(mMockContext).enforceCallingOrSelfPermission(
+                    eq(android.Manifest.permission.SET_TIME), anyString());
+        }
+    }
+
+    @Test
+    public void testSuggestNetworkTime() throws Exception {
+        doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
+
+        NetworkTimeSuggestion NetworkTimeSuggestion = createNetworkTimeSuggestion();
+        mTimeDetectorService.suggestNetworkTime(NetworkTimeSuggestion);
+        mTestHandler.assertTotalMessagesEnqueued(1);
+
+        verify(mMockContext).enforceCallingOrSelfPermission(
+                eq(android.Manifest.permission.SET_TIME), anyString());
+
+        mTestHandler.waitForEmptyQueue();
+        mStubbedTimeDetectorStrategy.verifySuggestNetworkTimeCalled(NetworkTimeSuggestion);
+    }
+
     @Test
     public void testDump() {
         when(mMockContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP))
@@ -146,11 +211,17 @@
         return new ManualTimeSuggestion(timeValue);
     }
 
+    private static NetworkTimeSuggestion createNetworkTimeSuggestion() {
+        TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
+        return new NetworkTimeSuggestion(timeValue);
+    }
+
     private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy {
 
         // Call tracking.
         private PhoneTimeSuggestion mLastPhoneSuggestion;
         private ManualTimeSuggestion mLastManualSuggestion;
+        private NetworkTimeSuggestion mLastNetworkSuggestion;
         private boolean mLastAutoTimeDetectionToggleCalled;
         private boolean mDumpCalled;
 
@@ -171,6 +242,12 @@
         }
 
         @Override
+        public void suggestNetworkTime(NetworkTimeSuggestion timeSuggestion) {
+            resetCallTracking();
+            mLastNetworkSuggestion = timeSuggestion;
+        }
+
+        @Override
         public void handleAutoTimeDetectionChanged() {
             resetCallTracking();
             mLastAutoTimeDetectionToggleCalled = true;
@@ -185,6 +262,7 @@
         void resetCallTracking() {
             mLastPhoneSuggestion = null;
             mLastManualSuggestion = null;
+            mLastNetworkSuggestion = null;
             mLastAutoTimeDetectionToggleCalled = false;
             mDumpCalled = false;
         }
@@ -197,6 +275,10 @@
             assertEquals(expectedSuggestion, mLastManualSuggestion);
         }
 
+        public void verifySuggestNetworkTimeCalled(NetworkTimeSuggestion expectedSuggestion) {
+            assertEquals(expectedSuggestion, mLastNetworkSuggestion);
+        }
+
         void verifyHandleAutoTimeDetectionToggleCalled() {
             assertTrue(mLastAutoTimeDetectionToggleCalled);
         }
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
index 1aa3d8f..ca6fd08 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.fail;
 
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.NetworkTimeSuggestion;
 import android.app.timedetector.PhoneTimeSuggestion;
 import android.content.Intent;
 import android.icu.util.Calendar;
@@ -45,14 +46,16 @@
     private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
             new TimestampedValue<>(
                     123456789L /* realtimeClockMillis */,
-                    createUtcTime(1977, 1, 1, 12, 0, 0));
+                    createUtcTime(2008, 5, 23, 12, 0, 0));
 
+    /**
+     * An arbitrary time, very different from the {@link #ARBITRARY_CLOCK_INITIALIZATION_INFO}
+     * time. Can be used as the basis for time suggestions.
+     */
     private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
 
     private static final int ARBITRARY_PHONE_ID = 123456;
 
-    private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
-
     private Script mScript;
 
     @Before
@@ -67,15 +70,16 @@
 
         int phoneId = ARBITRARY_PHONE_ID;
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+
         PhoneTimeSuggestion timeSuggestion =
                 mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
-        int clockIncrement = 1000;
-        long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+        mScript.simulateTimePassing()
+                .simulatePhoneTimeSuggestion(timeSuggestion);
 
-        mScript.simulateTimePassing(clockIncrement)
-                .simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+        long expectedSystemClockMillis =
+                mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+        mScript.verifySystemClockWasSetAndResetCallTracking(
+                expectedSystemClockMillis, true /* expectNetworkBroadcast */)
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
     }
 
@@ -94,26 +98,24 @@
 
     @Test
     public void testSuggestPhoneTime_systemClockThreshold() {
-        int systemClockUpdateThresholdMillis = 1000;
+        final int systemClockUpdateThresholdMillis = 1000;
+        final int clockIncrementMillis = 100;
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeThresholds(systemClockUpdateThresholdMillis)
                 .pokeAutoTimeDetectionEnabled(true);
 
-        final int clockIncrement = 100;
         int phoneId = ARBITRARY_PHONE_ID;
 
         // Send the first time signal. It should be used.
         {
-            long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
             PhoneTimeSuggestion timeSuggestion1 =
-                    mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
-            TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+                    mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
 
             // Increment the the device clocks to simulate the passage of time.
-            mScript.simulateTimePassing(clockIncrement);
+            mScript.simulateTimePassing(clockIncrementMillis);
 
             long expectedSystemClockMillis1 =
-                    TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+                    mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
                     .verifySystemClockWasSetAndResetCallTracking(
@@ -127,7 +129,7 @@
             int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
             PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
                     phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
-            mScript.simulateTimePassing(clockIncrement)
+            mScript.simulateTimePassing(clockIncrementMillis)
                     .simulatePhoneTimeSuggestion(timeSuggestion2)
                     .verifySystemClockWasNotSetAndResetCallTracking()
                     .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
@@ -138,11 +140,10 @@
             PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
                     phoneId,
                     mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
-            mScript.simulateTimePassing(clockIncrement);
+            mScript.simulateTimePassing(clockIncrementMillis);
 
             long expectedSystemClockMillis3 =
-                    TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(),
-                            mScript.peekElapsedRealtimeMillis());
+                    mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
                     .verifySystemClockWasSetAndResetCallTracking(
@@ -162,17 +163,16 @@
         int phone1Id = ARBITRARY_PHONE_ID;
         int phone2Id = ARBITRARY_PHONE_ID + 1;
         long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        long phone2TimeMillis = phone1TimeMillis + 60000;
-
-        final int clockIncrement = 999;
+        long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis();
 
         // Make a suggestion with phone2Id.
         {
             PhoneTimeSuggestion phone2TimeSuggestion =
                     mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
-            mScript.simulateTimePassing(clockIncrement);
+            mScript.simulateTimePassing();
 
-            long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+            long expectedSystemClockMillis =
+                    mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
                     .verifySystemClockWasSetAndResetCallTracking(
@@ -181,15 +181,16 @@
                     .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
         }
 
-        mScript.simulateTimePassing(clockIncrement);
+        mScript.simulateTimePassing();
 
         // Now make a different suggestion with phone1Id.
         {
             PhoneTimeSuggestion phone1TimeSuggestion =
                     mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
-            mScript.simulateTimePassing(clockIncrement);
+            mScript.simulateTimePassing();
 
-            long expectedSystemClockMillis = phone1TimeMillis + clockIncrement;
+            long expectedSystemClockMillis =
+                    mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
                     .verifySystemClockWasSetAndResetCallTracking(
@@ -198,14 +199,14 @@
 
         }
 
-        mScript.simulateTimePassing(clockIncrement);
+        mScript.simulateTimePassing();
 
         // Make another suggestion with phone2Id. It should be stored but not used because the
         // phone1Id suggestion will still "win".
         {
             PhoneTimeSuggestion phone2TimeSuggestion =
                     mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
-            mScript.simulateTimePassing(clockIncrement);
+            mScript.simulateTimePassing();
 
             mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
                     .verifySystemClockWasNotSetAndResetCallTracking()
@@ -220,9 +221,10 @@
         {
             PhoneTimeSuggestion phone2TimeSuggestion =
                     mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
-            mScript.simulateTimePassing(clockIncrement);
+            mScript.simulateTimePassing();
 
-            long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+            long expectedSystemClockMillis =
+                    mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime());
 
             mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
                     .verifySystemClockWasSetAndResetCallTracking(
@@ -239,7 +241,7 @@
         int phoneId = ARBITRARY_PHONE_ID;
         PhoneTimeSuggestion timeSuggestion =
                 mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
-        mScript.simulateTimePassing(1000)
+        mScript.simulateTimePassing()
                 .simulatePhoneTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasNotSetAndResetCallTracking()
                 .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
@@ -260,9 +262,8 @@
         TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
 
         // Initialize the strategy / device with a time set from a phone suggestion.
-        mScript.simulateTimePassing(100);
-        long expectedSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+        mScript.simulateTimePassing();
+        long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
         mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
                 .verifySystemClockWasSetAndResetCallTracking(
                         expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
@@ -299,8 +300,7 @@
         long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
         TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
                 validReferenceTimeMillis, validUtcTimeMillis);
-        long expectedSystemClockMillis4 =
-                TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+        long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4);
         PhoneTimeSuggestion timeSuggestion4 =
                 createPhoneTimeSuggestion(phoneId, utcTime4);
         mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
@@ -335,8 +335,7 @@
         // Simulate more time passing.
         mScript.simulateTimePassing(clockIncrementMillis);
 
-        long expectedSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+        long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1);
 
         // Turn on auto time detection.
         mScript.simulateAutoTimeDetectionToggle()
@@ -357,8 +356,8 @@
         // Simulate more time passing.
         mScript.simulateTimePassing(clockIncrementMillis);
 
-        long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt(
-                timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis());
+        long expectedSystemClockMillis2 =
+                mScript.calculateTimeInMillisForNow(timeSuggestion2.getUtcTime());
 
         // The new time, though valid, should not be set in the system clock because auto time is
         // disabled.
@@ -382,19 +381,21 @@
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
         PhoneTimeSuggestion phoneSuggestion =
                 mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
-        int clockIncrementMillis = 1000;
 
-        mScript.simulateTimePassing(clockIncrementMillis)
-                .simulatePhoneTimeSuggestion(phoneSuggestion)
+        mScript.simulateTimePassing();
+
+        long expectedSystemClockMillis =
+                mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime());
+        mScript.simulatePhoneTimeSuggestion(phoneSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
-                        testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */)
+                        expectedSystemClockMillis, true /* expectedNetworkBroadcast */)
                 .assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
 
         // Look inside and check what the strategy considers the current best phone suggestion.
         assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
 
         // Simulate time passing, long enough that phoneSuggestion is now too old.
-        mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_MAX_AGE_MILLIS);
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS);
 
         // Look inside and check what the strategy considers the current best phone suggestion. It
         // should still be the, it's just no longer used.
@@ -407,13 +408,14 @@
         mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
                 .pokeAutoTimeDetectionEnabled(false);
 
-        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
-        ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis);
-        final int clockIncrement = 1000;
-        long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+        ManualTimeSuggestion timeSuggestion =
+                mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
 
-        mScript.simulateTimePassing(clockIncrement)
-                .simulateManualTimeSuggestion(timeSuggestion)
+        mScript.simulateTimePassing();
+
+        long expectedSystemClockMillis =
+                mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+        mScript.simulateManualTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
                         expectedSystemClockMillis, false /* expectNetworkBroadcast */);
     }
@@ -430,21 +432,19 @@
         long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
         PhoneTimeSuggestion phoneTimeSuggestion =
                 mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
-        long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
-        final int clockIncrement = 1000;
 
         // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
+        mScript.simulateTimePassing();
 
+        long expectedAutoClockMillis =
+                mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
         mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
                         expectedAutoClockMillis, true /* expectNetworkBroadcast */)
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
 
         // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
+        mScript.simulateTimePassing();
 
         // Switch to manual.
         mScript.simulateAutoTimeDetectionToggle()
@@ -452,26 +452,29 @@
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
 
         // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
+        mScript.simulateTimePassing();
 
         // Simulate a manual suggestion 1 day different from the auto suggestion.
-        long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS;
-        long expectedManualClockMillis = manualTimeMillis;
+        long manualTimeMillis = testTimeMillis + Duration.ofDays(1).toMillis();
         ManualTimeSuggestion manualTimeSuggestion =
                 mScript.generateManualTimeSuggestion(manualTimeMillis);
+        mScript.simulateTimePassing();
+
+        long expectedManualClockMillis =
+                mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime());
         mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
                 .verifySystemClockWasSetAndResetCallTracking(
                         expectedManualClockMillis, false /* expectNetworkBroadcast */)
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
 
         // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
+        mScript.simulateTimePassing();
 
         // Switch back to auto.
         mScript.simulateAutoTimeDetectionToggle();
 
+        expectedAutoClockMillis =
+                mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime());
         mScript.verifySystemClockWasSetAndResetCallTracking(
                         expectedAutoClockMillis, true /* expectNetworkBroadcast */)
                 .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
@@ -492,13 +495,143 @@
 
         ManualTimeSuggestion timeSuggestion =
                 mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
-        final int clockIncrement = 1000;
 
-        mScript.simulateTimePassing(clockIncrement)
+        mScript.simulateTimePassing()
                 .simulateManualTimeSuggestion(timeSuggestion)
                 .verifySystemClockWasNotSetAndResetCallTracking();
     }
 
+    @Test
+    public void testSuggestNetworkTime_autoTimeEnabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        NetworkTimeSuggestion timeSuggestion =
+                mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
+
+        mScript.simulateTimePassing();
+
+        long expectedSystemClockMillis =
+                mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime());
+        mScript.simulateNetworkTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+    }
+
+    @Test
+    public void testSuggestNetworkTime_autoTimeDisabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        NetworkTimeSuggestion timeSuggestion =
+                mScript.generateNetworkTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
+
+        mScript.simulateTimePassing()
+                .simulateNetworkTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    @Test
+    public void testSuggestNetworkTime_phoneSuggestionsBeatNetworkSuggestions() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        // Three obviously different times that could not be mistaken for each other.
+        long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS;
+        long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis();
+        long phoneTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis();
+        // A small increment used to simulate the passage of time, but not enough to interfere with
+        // macro-level time changes associated with suggestion age.
+        final long smallTimeIncrementMillis = 101;
+
+        // A network suggestion is made. It should be used because there is no phone suggestion.
+        NetworkTimeSuggestion networkTimeSuggestion1 =
+                mScript.generateNetworkTimeSuggestion(networkTimeMillis1);
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .simulateNetworkTimeSuggestion(networkTimeSuggestion1)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime()),
+                        false /* expectNetworkBroadcast */);
+
+        // Check internal state.
+        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null)
+                .assertLatestNetworkSuggestion(networkTimeSuggestion1);
+        assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
+        assertNull(mScript.peekBestPhoneSuggestion());
+
+        // Simulate a little time passing.
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Now a phone suggestion is made. Phone suggestions are prioritized over network
+        // suggestions so it should "win".
+        PhoneTimeSuggestion phoneTimeSuggestion =
+                mScript.generatePhoneTimeSuggestion(ARBITRARY_PHONE_ID, phoneTimeMillis);
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .simulatePhoneTimeSuggestion(phoneTimeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()),
+                        true /* expectNetworkBroadcast */);
+
+        // Check internal state.
+        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+                .assertLatestNetworkSuggestion(networkTimeSuggestion1);
+        assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion());
+        assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
+
+        // Simulate some significant time passing: half the time allowed before a time signal
+        // becomes "too old to use".
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Now another network suggestion is made. Phone suggestions are prioritized over network
+        // suggestions so the latest phone suggestion should still "win".
+        NetworkTimeSuggestion networkTimeSuggestion2 =
+                mScript.generateNetworkTimeSuggestion(networkTimeMillis2);
+        mScript.simulateTimePassing(smallTimeIncrementMillis)
+                .simulateNetworkTimeSuggestion(networkTimeSuggestion2)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Check internal state.
+        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+                .assertLatestNetworkSuggestion(networkTimeSuggestion2);
+        assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
+        assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion());
+
+        // Simulate some significant time passing: half the time allowed before a time signal
+        // becomes "too old to use". This should mean that phoneTimeSuggestion is now too old to be
+        // used but networkTimeSuggestion2 is not.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2);
+
+        // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last
+        // suggestion it used becomes too old: it requires a new suggestion or an auto-time toggle
+        // to re-run the detection logic. This may change in future but until then we rely on a
+        // steady stream of suggestions to re-evaluate.
+        mScript.verifySystemClockWasNotSetAndResetCallTracking();
+
+        // Check internal state.
+        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+                .assertLatestNetworkSuggestion(networkTimeSuggestion2);
+        assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
+        assertNull(mScript.peekBestPhoneSuggestion());
+
+        // Toggle auto-time off and on to force the detection logic to run.
+        mScript.simulateAutoTimeDetectionToggle()
+                .simulateTimePassing(smallTimeIncrementMillis)
+                .simulateAutoTimeDetectionToggle();
+
+        // Verify the latest network time now wins.
+        mScript.verifySystemClockWasSetAndResetCallTracking(
+                mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime()),
+                false /* expectNetworkTimeBroadcast */);
+
+        // Check internal state.
+        mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion)
+                .assertLatestNetworkSuggestion(networkTimeSuggestion2);
+        assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion());
+        assertNull(mScript.peekBestPhoneSuggestion());
+    }
+
     /**
      * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
      * like the real thing should, it also asserts preconditions.
@@ -674,6 +807,11 @@
             return this;
         }
 
+        Script simulateNetworkTimeSuggestion(NetworkTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestNetworkTime(timeSuggestion);
+            return this;
+        }
+
         Script simulateAutoTimeDetectionToggle() {
             mFakeCallback.simulateAutoTimeZoneDetectionToggle();
             mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
@@ -685,6 +823,13 @@
             return this;
         }
 
+        /**
+         * Simulates time passing by an arbitrary (but relatively small) amount.
+         */
+        Script simulateTimePassing() {
+            return simulateTimePassing(999);
+        }
+
         Script verifySystemClockWasNotSetAndResetCallTracking() {
             mFakeCallback.verifySystemClockNotSet();
             mFakeCallback.verifyIntentWasNotBroadcast();
@@ -711,14 +856,30 @@
         }
 
         /**
+         * White box test info: Asserts the latest network suggestion is as expected.
+         */
+        Script assertLatestNetworkSuggestion(NetworkTimeSuggestion expected) {
+            assertEquals(expected, mTimeDetectorStrategy.getLatestNetworkSuggestion());
+            return this;
+        }
+
+        /**
          * White box test info: Returns the phone suggestion that would be used, if any, given the
-         * current elapsed real time clock.
+         * current elapsed real time clock and regardless of origin prioritization.
          */
         PhoneTimeSuggestion peekBestPhoneSuggestion() {
             return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
         }
 
         /**
+         * White box test info: Returns the network suggestion that would be used, if any, given the
+         * current elapsed real time clock and regardless of origin prioritization.
+         */
+        NetworkTimeSuggestion peekLatestValidNetworkSuggestion() {
+            return mTimeDetectorStrategy.findLatestValidNetworkSuggestionForTests();
+        }
+
+        /**
          * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
          * reference time.
          */
@@ -739,6 +900,24 @@
             }
             return createPhoneTimeSuggestion(phoneId, time);
         }
+
+        /**
+         * Generates a NetworkTimeSuggestion using the current elapsed realtime clock for the
+         * reference time.
+         */
+        NetworkTimeSuggestion generateNetworkTimeSuggestion(long timeMillis) {
+            TimestampedValue<Long> utcTime =
+                    new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis);
+            return new NetworkTimeSuggestion(utcTime);
+        }
+
+        /**
+         * Calculates what the supplied time would be when adjusted for the movement of the fake
+         * elapsed realtime clock.
+         */
+        long calculateTimeInMillisForNow(TimestampedValue<Long> utcTime) {
+            return TimeDetectorStrategy.getTimeAt(utcTime, peekElapsedRealtimeMillis());
+        }
     }
 
     private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp
new file mode 100644
index 0000000..9aaa37d
--- /dev/null
+++ b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp
@@ -0,0 +1,21 @@
+// 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.
+
+android_test_helper_app {
+    name: "AppIntegrityManagerServiceTestApp",
+
+    test_suites: ["device-tests"],
+
+    certificate: "platform",
+}
diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..f5dbf43
--- /dev/null
+++ b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 Google Inc.
+ *
+ * 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.google.android.appintegritymanager.test.app"
+    android:versionCode="5000">
+
+    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="28" />
+
+    <application android:hasCode="false">
+        <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb|"/>
+    </application>
+</manifest>
+
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 95617b1..172df99 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -492,23 +492,13 @@
         return sbn;
     }
 
-
     private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
             String groupKey, boolean isSummary) {
-        return generateNotificationRecord(channel, id, groupKey, isSummary, false /* isBubble */);
-    }
-
-    private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
-            String groupKey, boolean isSummary, boolean isBubble) {
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
                 .setGroup(groupKey)
                 .setGroupSummary(isSummary);
-        if (isBubble) {
-            nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
-        }
-
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
                 "tag" + System.currentTimeMillis(), mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
@@ -521,11 +511,6 @@
 
     private NotificationRecord generateNotificationRecord(NotificationChannel channel,
             Notification.TvExtender extender) {
-        return generateNotificationRecord(channel, extender, false /* isBubble */);
-    }
-
-    private NotificationRecord generateNotificationRecord(NotificationChannel channel,
-            Notification.TvExtender extender, boolean isBubble) {
         if (channel == null) {
             channel = mTestNotificationChannel;
         }
@@ -535,9 +520,6 @@
         if (extender != null) {
             nb.extend(extender);
         }
-        if (isBubble) {
-            nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
-        }
         StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         return new NotificationRecord(mContext, sbn, channel);
@@ -555,6 +537,26 @@
         return new NotificationRecord(mContext, sbn, channel);
     }
 
+    private NotificationRecord generateMessageBubbleNotifRecord(NotificationChannel channel,
+            String tag) {
+        return generateMessageBubbleNotifRecord(true, channel, 1, tag, null, false);
+    }
+
+    private NotificationRecord generateMessageBubbleNotifRecord(boolean addMetadata,
+            NotificationChannel channel, int id, String tag, String groupKey, boolean isSummary) {
+        if (channel == null) {
+            channel = mTestNotificationChannel;
+        }
+        if (tag == null) {
+            tag = "tag";
+        }
+        Notification.Builder nb = getMessageStyleNotifBuilder(addMetadata, groupKey, isSummary);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
+                tag, mUid, 0,
+                nb.build(), new UserHandle(mUid), null, 0);
+        return new NotificationRecord(mContext, sbn, channel);
+    }
+
     private Map<String, Answer> getSignalExtractorSideEffects() {
         Map<String, Answer> answers = new ArrayMap<>();
 
@@ -610,23 +612,57 @@
                 false);
     }
 
-    private Notification.BubbleMetadata.Builder getBasicBubbleMetadataBuilder() {
+    private Notification.BubbleMetadata.Builder getBubbleMetadataBuilder() {
         PendingIntent pi = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
         return new Notification.BubbleMetadata.Builder()
                 .setIntent(pi)
                 .setIcon(Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon));
     }
 
+    private Notification.Builder getMessageStyleNotifBuilder(boolean addBubbleMetadata,
+            String groupKey, boolean isSummary) {
+        // Give it a person
+        Person person = new Person.Builder()
+                .setName("bubblebot")
+                .build();
+        // It needs remote input to be bubble-able
+        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
+        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
+        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
+        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
+                inputIntent).addRemoteInput(remoteInput)
+                .build();
+        // Make it messaging style
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setStyle(new Notification.MessagingStyle(person)
+                        .setConversationTitle("Bubble Chat")
+                        .addMessage("Hello?",
+                                SystemClock.currentThreadTimeMillis() - 300000, person)
+                        .addMessage("Is it me you're looking for?",
+                                SystemClock.currentThreadTimeMillis(), person)
+                )
+                .setActions(replyAction)
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setGroupSummary(isSummary);
+        if (groupKey != null) {
+            nb.setGroup(groupKey);
+        }
+        if (addBubbleMetadata) {
+            nb.setBubbleMetadata(getBubbleMetadataBuilder().build());
+        }
+        return nb;
+    }
+
     private NotificationRecord addGroupWithBubblesAndValidateAdded(boolean summaryAutoCancel)
             throws RemoteException {
 
-        // Notification that has bubble metadata
-        NotificationRecord nrBubble = generateNotificationRecord(mTestNotificationChannel, 1,
-                "BUBBLE_GROUP", false /* isSummary */, true /* isBubble */);
+        String groupKey = "BUBBLE_GROUP";
 
-        // Make the package foreground so that we're allowed to be a bubble
-        when(mActivityManager.getPackageImportance(nrBubble.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
+        // Notification that has bubble metadata
+        NotificationRecord nrBubble = generateMessageBubbleNotifRecord(true /* addMetadata */,
+                mTestNotificationChannel, 1 /* id */, "tag", groupKey, false /* isSummary */);
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.sbn.getTag(),
                 nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
@@ -637,9 +673,10 @@
         assertEquals(1, notifsAfter.length);
         assertTrue((notifsAfter[0].getNotification().flags & FLAG_BUBBLE) != 0);
 
-        // Plain notification without bubble metadata
-        NotificationRecord nrPlain = generateNotificationRecord(mTestNotificationChannel, 2,
-                "BUBBLE_GROUP", false /* isSummary */, false /* isBubble */);
+        // Notification without bubble metadata
+        NotificationRecord nrPlain = generateMessageBubbleNotifRecord(false /* addMetadata */,
+                mTestNotificationChannel, 2 /* id */, "tag", groupKey, false /* isSummary */);
+
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.sbn.getTag(),
                 nrPlain.sbn.getId(), nrPlain.sbn.getNotification(), nrPlain.sbn.getUserId());
         waitForIdle();
@@ -648,8 +685,9 @@
         assertEquals(2, notifsAfter.length);
 
         // Summary notification for both of those
-        NotificationRecord nrSummary = generateNotificationRecord(mTestNotificationChannel, 3,
-                "BUBBLE_GROUP", true /* isSummary */, false /* isBubble */);
+        NotificationRecord nrSummary = generateMessageBubbleNotifRecord(false /* addMetadata */,
+                mTestNotificationChannel, 3 /* id */, "tag", groupKey, true /* isSummary */);
+
         if (summaryAutoCancel) {
             nrSummary.getNotification().flags |= FLAG_AUTO_CANCEL;
         }
@@ -4708,13 +4746,8 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        // Say we're foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
+        NotificationRecord nr =
+                generateMessageBubbleNotifRecord(mTestNotificationChannel, "testFlagBubble");
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -4732,13 +4765,8 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, false /* app */, true /* channel */);
 
-        // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        // Say we're foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                        "testFlagBubble_noFlag_appNotAllowed");
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -4752,174 +4780,40 @@
     }
 
     @Test
-    public void testFlagBubbleNotifs_flag_appForeground() throws RemoteException {
+    public void testFlagBubbleNotifs_noFlag_whenAppForeground() throws RemoteException {
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setBubbleMetadata(getBubbleMetadataBuilder().build());
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+                nb.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         // Say we're foreground
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
-
-        mBinderService.enqueueNotificationWithTag(PKG, PKG,
-                nr.sbn.getTag(), nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
-        waitForIdle();
-
-        // yes allowed, yes foreground, yes bubble
-        assertTrue(mService.getNotificationRecord(
-                nr.sbn.getKey()).getNotification().isBubbleNotification());
-    }
-
-    @Test
-    public void testFlagBubbleNotifs_noFlag_appNotForeground() throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
-
-        // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        // Make sure we're NOT foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_VISIBLE);
-
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
-        // yes allowed but NOT foreground, no bubble
+        // if notif isn't configured properly it doesn't get to bubble just because app is
+        // foreground.
         assertFalse(mService.getNotificationRecord(
                 nr.sbn.getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
-    public void testFlagBubbleNotifs_flag_previousForegroundFlag() throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
-
-        // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        // Send notif when we're foreground
-        when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr1.sbn.getTag(),
-                nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
-        waitForIdle();
-
-        // yes allowed, yes foreground, yes bubble
-        assertTrue(mService.getNotificationRecord(
-                nr1.sbn.getKey()).getNotification().isBubbleNotification());
-
-        // Send a new update when we're not foreground
-        NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_VISIBLE);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
-                nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
-        waitForIdle();
-
-        // yes allowed, previously foreground / flagged, yes bubble
-        assertTrue(mService.getNotificationRecord(
-                nr2.sbn.getKey()).getNotification().isBubbleNotification());
-
-        StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifs2.length);
-        assertEquals(1, mService.getNotificationRecordCount());
-    }
-
-    @Test
-    public void testFlagBubbleNotifs_noFlag_previousForegroundFlag_afterRemoval()
-            throws RemoteException {
-        // Bubbles are allowed!
-        setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
-
-        // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr1 = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        // Send notif when we're foreground
-        when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr1.sbn.getTag(),
-                nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
-        waitForIdle();
-
-        // yes allowed, yes foreground, yes bubble
-        assertTrue(mService.getNotificationRecord(
-                nr1.sbn.getKey()).getNotification().isBubbleNotification());
-
-        // Remove the bubble
-        mBinderService.cancelNotificationWithTag(PKG, PKG, nr1.sbn.getTag(), nr1.sbn.getId(),
-                nr1.sbn.getUserId());
-        waitForIdle();
-
-        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
-        assertEquals(0, notifs.length);
-        assertEquals(0, mService.getNotificationRecordCount());
-
-        // Send a new update when we're not foreground
-        NotificationRecord nr2 = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_VISIBLE);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
-                nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
-        waitForIdle();
-
-        // yes allowed, but was removed & no foreground, so no bubble
-        assertFalse(mService.getNotificationRecord(
-                nr2.sbn.getKey()).getNotification().isBubbleNotification());
-
-        StatusBarNotification[] notifs2 = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifs2.length);
-        assertEquals(1, mService.getNotificationRecordCount());
-    }
-
-    @Test
     public void testFlagBubbleNotifs_flag_messaging() throws RemoteException {
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // It needs remote input to be bubble-able
-        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
-        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
-        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
-                inputIntent).addRemoteInput(remoteInput)
-                .build();
-        // Make it messaging style
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setStyle(new Notification.MessagingStyle(person)
-                        .setConversationTitle("Bubble Chat")
-                        .addMessage("Hello?",
-                                SystemClock.currentThreadTimeMillis() - 300000, person)
-                        .addMessage("Is it me you're looking for?",
-                                SystemClock.currentThreadTimeMillis(), person)
-                )
-                .setActions(replyAction)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_flag_messaging", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testFlagBubbleNotifs_flag_messaging");
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -4927,7 +4821,7 @@
 
         // yes allowed, yes messaging, yes bubble
         assertTrue(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
+                nr.sbn.getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -4936,7 +4830,7 @@
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
         // Give it a person
         Person person = new Person.Builder()
                 .setName("bubblebot")
@@ -4972,7 +4866,7 @@
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
         // Give it a person
         Person person = new Person.Builder()
                 .setName("bubblebot")
@@ -5005,7 +4899,7 @@
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
         // Make it a phone call
         Notification.Builder nb = new Notification.Builder(mContext,
                 mTestNotificationChannel.getId())
@@ -5036,7 +4930,7 @@
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
         // Give it a person
         Person person = new Person.Builder()
                 .setName("bubblebot")
@@ -5070,30 +4964,8 @@
         // Bubbles are NOT allowed!
         setUpPrefsForBubbles(false /* global */, true /* app */, true /* channel */);
 
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // Make it messaging style
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setStyle(new Notification.MessagingStyle(person)
-                        .setConversationTitle("Bubble Chat")
-                        .addMessage("Hello?",
-                                SystemClock.currentThreadTimeMillis() - 300000, person)
-                        .addMessage("Is it me you're looking for?",
-                                SystemClock.currentThreadTimeMillis(), person)
-                )
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_noFlag_messaging_appNotAllowed", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testFlagBubbleNotifs_noFlag_messaging_appNotAllowed");
 
         // Post the notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
@@ -5102,7 +4974,7 @@
 
         // not allowed, no bubble
         assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
+                nr.sbn.getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5110,8 +4982,14 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Notif WITHOUT bubble metadata
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+        // Messaging notif WITHOUT bubble metadata
+        Notification.Builder nb = getMessageStyleNotifBuilder(false /* addBubbleMetadata */,
+                null /* groupKey */, false /* isSummary */);
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_notBubble", mUid, 0,
+                nb.build(), new UserHandle(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         // Post the notification
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
@@ -5128,39 +5006,17 @@
         // Bubbles are allowed except on this channel
         setUpPrefsForBubbles(true /* global */, true /* app */, false /* channel */);
 
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // Make it messaging style
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setStyle(new Notification.MessagingStyle(person)
-                        .setConversationTitle("Bubble Chat")
-                        .addMessage("Hello?",
-                                SystemClock.currentThreadTimeMillis() - 300000, person)
-                        .addMessage("Is it me you're looking for?",
-                                SystemClock.currentThreadTimeMillis(), person)
-                )
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
-                "testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed", mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed");
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
         // channel not allowed, no bubble
         assertFalse(mService.getNotificationRecord(
-                sbn.getKey()).getNotification().isBubbleNotification());
+                nr.sbn.getKey()).getNotification().isBubbleNotification());
     }
 
     @Test
@@ -5169,7 +5025,7 @@
         setUpPrefsForBubbles(false /* global */, true /* app */, true /* channel */);
 
         // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
         // Give it a person
         Person person = new Person.Builder()
                 .setName("bubblebot")
@@ -5205,7 +5061,7 @@
         setUpPrefsForBubbles(true /* global */, true /* app */, false /* channel */);
 
         // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder().build();
+        Notification.BubbleMetadata data = getBubbleMetadataBuilder().build();
         // Give it a person
         Person person = new Person.Builder()
                 .setName("bubblebot")
@@ -5412,13 +5268,9 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Notif with bubble metadata but not our other misc requirements
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
-
-        // Say we're foreground
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
+        // Notif with bubble metadata
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testNotificationBubbleChanged_false");
 
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
@@ -5447,9 +5299,9 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Plain notification that has bubble metadata
+        // Notif that is not a bubble
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
+                1, null, false);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
@@ -5459,9 +5311,12 @@
         assertEquals(1, notifsBefore.length);
         assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
 
-        // Make the package foreground so that we're allowed to be a bubble
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
+        // Update the notification to be message style / meet bubble requirements
+        NotificationRecord nr2 = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                nr.sbn.getTag());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
+                nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
+        waitForIdle();
 
         // Reset as this is called when the notif is first sent
         reset(mListeners);
@@ -5482,8 +5337,7 @@
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
         // Notif that is not a bubble
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
+        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
@@ -5681,26 +5535,17 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Plain notification that has bubble metadata
-        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, true /* isBubble */);
+        // And we are low ram
+        when(mActivityManager.isLowRamDevice()).thenReturn(true);
+
+        // Notification that would typically bubble
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testNotificationBubbles_disabled_lowRamDevice");
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
-        // Would be a normal notification because wouldn't have met requirements to bubble
-        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
-        assertEquals(1, notifsBefore.length);
-        assertEquals((notifsBefore[0].getNotification().flags & FLAG_BUBBLE), 0);
-
-        // Make the package foreground so that we're allowed to be a bubble
-        when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
-                IMPORTANCE_FOREGROUND);
-
-        // And we are low ram
-        when(mActivityManager.isLowRamDevice()).thenReturn(true);
-
-        // We wouldn't be a bubble because the notification didn't meet requirements (low ram)
+        // But we wouldn't be a bubble because the device is low ram & all bubbles are disabled.
         StatusBarNotification[] notifsAfter = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifsAfter.length);
         assertEquals((notifsAfter[0].getNotification().flags & FLAG_BUBBLE), 0);
@@ -5774,50 +5619,23 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder()
-                .setSuppressNotification(true)
-                .setAutoExpandBubble(true).build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // It needs remote input to be bubble-able
-        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
-        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
-        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
-                inputIntent).addRemoteInput(remoteInput)
-                .build();
-        // Make it messaging style
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setStyle(new Notification.MessagingStyle(person)
-                        .setConversationTitle("Bubble Chat")
-                        .addMessage("Hello?",
-                                SystemClock.currentThreadTimeMillis() - 300000, person)
-                        .addMessage("Is it me you're looking for?",
-                                SystemClock.currentThreadTimeMillis(), person)
-                )
-                .setActions(replyAction)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testNotificationBubbles_flagAutoExpandForeground_fails_notForeground");
+        // Modify metadata flags
+        nr.sbn.getNotification().getBubbleMetadata().setFlags(
+                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
+                        | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
 
         // Ensure we're not foreground
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_VISIBLE);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
         // yes allowed, yes messaging, yes bubble
-        Notification notif = mService.getNotificationRecord(sbn.getKey()).getNotification();
+        Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
         assertTrue(notif.isBubbleNotification());
 
         // Our flags should have failed since we're not foreground
@@ -5831,53 +5649,26 @@
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
 
-        // Give it bubble metadata
-        Notification.BubbleMetadata data = getBasicBubbleMetadataBuilder()
-                .setSuppressNotification(true)
-                .setAutoExpandBubble(true).build();
-        // Give it a person
-        Person person = new Person.Builder()
-                .setName("bubblebot")
-                .build();
-        // It needs remote input to be bubble-able
-        RemoteInput remoteInput = new RemoteInput.Builder("reply_key").setLabel("reply").build();
-        PendingIntent inputIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0);
-        Icon icon = Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
-        Notification.Action replyAction = new Notification.Action.Builder(icon, "Reply",
-                inputIntent).addRemoteInput(remoteInput)
-                .build();
-        // Make it messaging style
-        Notification.Builder nb = new Notification.Builder(mContext,
-                mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setBubbleMetadata(data)
-                .setStyle(new Notification.MessagingStyle(person)
-                        .setConversationTitle("Bubble Chat")
-                        .addMessage("Hello?",
-                                SystemClock.currentThreadTimeMillis() - 300000, person)
-                        .addMessage("Is it me you're looking for?",
-                                SystemClock.currentThreadTimeMillis(), person)
-                )
-                .setActions(replyAction)
-                .setSmallIcon(android.R.drawable.sym_def_app_icon);
-
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
-                nb.build(), new UserHandle(mUid), null, 0);
-        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+        NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel,
+                "testNotificationBubbles_flagAutoExpandForeground_succeeds_foreground");
+        // Modify metadata flags
+        nr.sbn.getNotification().getBubbleMetadata().setFlags(
+                Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE
+                        | Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION);
 
         // Ensure we are in the foreground
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
         // yes allowed, yes messaging, yes bubble
-        Notification notif = mService.getNotificationRecord(sbn.getKey()).getNotification();
+        Notification notif = mService.getNotificationRecord(nr.sbn.getKey()).getNotification();
         assertTrue(notif.isBubbleNotification());
 
-        // Our flags should have failed since we are foreground
+        // Our flags should have passed since we are foreground
         assertTrue(notif.getBubbleMetadata().getAutoExpandBubble());
         assertTrue(notif.getBubbleMetadata().isNotificationSuppressed());
     }
@@ -6004,7 +5795,7 @@
     @Test
     public void testNotificationHistory_addNoisyNotification() throws Exception {
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
-                null /* tvExtender */, false);
+                null /* tvExtender */);
         mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index d6bd1d0..73b2f6b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -902,7 +902,7 @@
         topActivity.mVisibleRequested = false;
         topActivity.nowVisible = false;
         topActivity.finishing = true;
-        topActivity.setState(PAUSED, "true");
+        topActivity.setState(STOPPED, "true");
         // Mark the bottom activity as not visible, so that we would wait for it before removing
         // the top one.
         mActivity.mVisibleRequested = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index c24ce2b..a5157fe9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -236,7 +236,7 @@
         // Overlay must be for a different user to prevent recognizing a matching top activity
         final ActivityRecord taskOverlay = new ActivityBuilder(mService).setTask(task)
                 .setUid(UserHandle.PER_USER_RANGE * 2).build();
-        taskOverlay.mTaskOverlay = true;
+        taskOverlay.setTaskOverlay(true);
 
         final RootWindowContainer.FindTaskResult result =
                 new RootWindowContainer.FindTaskResult();
@@ -574,6 +574,27 @@
     }
 
     @Test
+    public void testShouldBeVisible_FullscreenBehindTranslucentInHomeStack() {
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+
+        final ActivityRecord firstActivity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        final Task task = firstActivity.getTask();
+        final ActivityRecord secondActivity = new ActivityBuilder(mService)
+                .setTask(task)
+                .build();
+
+        doReturn(false).when(secondActivity).occludesParent();
+        homeStack.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                false /* preserveWindows */);
+
+        assertTrue(firstActivity.shouldBeVisible());
+    }
+
+    @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
         mDefaultDisplay.removeStack(mStack);
 
@@ -865,7 +886,7 @@
                 .setComponent(new ComponentName("package.overlay", ".OverlayActivity")).build();
         // If the task only remains overlay activity, the task should also be removed.
         // See {@link ActivityStack#removeFromHistory}.
-        overlayActivity.mTaskOverlay = true;
+        overlayActivity.setTaskOverlay(true);
 
         // The activity without an app means it will be removed immediately.
         // See {@link ActivityStack#destroyActivityLocked}.
@@ -893,7 +914,7 @@
         // Making the first activity a task overlay means it will be removed from the task's
         // activities as well once second activity is removed as handleAppDied processes the
         // activity list in reverse.
-        firstActivity.mTaskOverlay = true;
+        firstActivity.setTaskOverlay(true);
         firstActivity.app = null;
 
         // second activity will be immediately removed as it has no state.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index b1132f6..9e54f40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -33,8 +33,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
+import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations;
@@ -433,32 +435,28 @@
     }
 
     /**
-     * This test ensures that if the intent is being delivered to a
+     * This test ensures that if the intent is being delivered to a split-screen unfocused task
+     * while it already on top, reports it as delivering to top.
      */
     @Test
     public void testSplitScreenDeliverToTop() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
-        final ActivityRecord focusActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        // Create reusable activity after entering split-screen so that it is the top secondary
-        // stack.
-        reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        final ActivityStarter starter = prepareStarter(
+                FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+        final ActivityRecord splitPrimaryFocusActivity =
+                new ActivityBuilder(mService).setCreateTask(true).build();
+        final ActivityRecord splitSecondReusableActivity =
+                new ActivityBuilder(mService).setCreateTask(true).build();
+        splitPrimaryFocusActivity.getActivityStack()
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        splitSecondReusableActivity.getActivityStack()
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
         // Set focus back to primary.
-        final ActivityStack focusStack = focusActivity.getActivityStack();
-        focusStack.moveToFront("testSplitScreenDeliverToTop");
+        splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenDeliverToTop");
 
-        doReturn(reusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
-
+        // Start activity and delivered new intent.
+        starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
+        doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
         final int result = starter.setReason("testSplitScreenDeliverToTop").execute();
 
         // Ensure result is delivering intent to top.
@@ -471,26 +469,30 @@
      */
     @Test
     public void testSplitScreenTaskToFront() {
-        final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        final ActivityStarter starter = prepareStarter(
+                FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | FLAG_ACTIVITY_SINGLE_TOP, false);
+        final ActivityRecord splitSecondReusableActivity =
+                new ActivityBuilder(mService).setCreateTask(true).build();
+        final ActivityRecord splitSecondTopActivity =
+                new ActivityBuilder(mService).setCreateTask(true).build();
+        final ActivityRecord splitPrimaryFocusActivity =
+                new ActivityBuilder(mService).setCreateTask(true).build();
+        splitPrimaryFocusActivity.getActivityStack()
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        splitSecondReusableActivity.getActivityStack()
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        splitSecondTopActivity.getActivityStack()
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
-        // Create reusable activity here first. Setting the windowing mode of the primary stack
-        // will move the existing standard full screen stack to secondary, putting this one on the
-        // bottom.
-        final ActivityRecord reusableActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
+        // Make it on top of split-screen-secondary.
+        splitSecondTopActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
 
-        reusableActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        // Let primary stack has focus.
+        splitPrimaryFocusActivity.getActivityStack().moveToFront("testSplitScreenTaskToFront");
 
-        final ActivityRecord focusActivity = new ActivityBuilder(mService)
-                .setCreateTask(true)
-                .build();
-
-        // Enter split-screen. Primary stack should have focus.
-        focusActivity.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-
-        doReturn(reusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
-
+        // Start activity and delivered new intent.
+        starter.getIntent().setComponent(splitSecondReusableActivity.mActivityComponent);
+        doReturn(splitSecondReusableActivity).when(mRootWindowContainer).findTask(any(), anyInt());
         final int result = starter.setReason("testSplitScreenMoveToFront").execute();
 
         // Ensure result is moving task to front.
@@ -979,4 +981,29 @@
         assertThat(result == START_SUCCESS).isTrue();
         assertThat(starter.mAddingToTask).isTrue();
     }
+
+    @Test
+    public void testTargetStackInSplitScreen() {
+        final ActivityStarter starter =
+                prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetLaunchStack */);
+        final ActivityRecord top = new ActivityBuilder(mService).setCreateTask(true).build();
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        final ActivityRecord[] outActivity = new ActivityRecord[1];
+
+        // Activity must not land on split-screen stack if currently not in split-screen mode.
+        starter.setActivityOptions(options.toBundle())
+                .setReason("testWindowingModeOptionsLaunchAdjacent")
+                .setOutActivity(outActivity).execute();
+        assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
+
+        // Move activity to split-screen-primary stack and make sure it has the focus.
+        top.getActivityStack().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        top.getActivityStack().moveToFront("testWindowingModeOptionsLaunchAdjacent");
+
+        // Activity must landed on split-screen-secondary when launch adjacent.
+        starter.setActivityOptions(options.toBundle())
+                .setReason("testWindowingModeOptionsLaunchAdjacent")
+                .setOutActivity(outActivity).execute();
+        assertThat(outActivity[0].inSplitScreenSecondaryWindowingMode()).isTrue();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
index a39be56..574517a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AnimatingActivityRegistryTest.java
@@ -28,11 +28,11 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -44,6 +44,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class AnimatingActivityRegistryTest extends WindowTestsBase {
 
     @Mock
@@ -60,7 +61,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 144611135)
     public void testDeferring() {
         final ActivityRecord activity1 = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -83,7 +83,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testContainerRemoved() {
         final ActivityRecord window1 = createActivityRecord(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index fc94c5e..8c2b293 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -61,7 +61,7 @@
     public void setUpOnDisplay(DisplayContent dc) {
         mActivity = createTestActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD);
         mTask = mActivity.getTask();
-        mStack = mTask.getTaskStack();
+        mStack = mTask.getStack();
 
         // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests.
         RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 0758eeb..73dd2df 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -126,7 +126,7 @@
         waitUntilHandlersIdle();
 
         exitingApp.mIsExiting = true;
-        exitingApp.getTask().getTaskStack().mExitingActivities.add(exitingApp);
+        exitingApp.getTask().getStack().mExitingActivities.add(exitingApp);
 
         assertForAllWindowsOrder(Arrays.asList(
                 mWallpaperWindow,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 5aece45..0527561 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -594,6 +594,8 @@
 
     @Test
     public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() {
+        assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_NONE);
+
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
         mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 31b658a..cf1f0a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -424,7 +424,7 @@
         void saveTask(Task task) {
             final int userId = task.mUserId;
             final ComponentName realActivity = task.realActivity;
-            mTmpParams.mPreferredDisplayId = task.getStack().mDisplayId;
+            mTmpParams.mPreferredDisplayId = task.getDisplayId();
             mTmpParams.mWindowingMode = task.getWindowingMode();
             if (task.mLastNonFullscreenBounds != null) {
                 mTmpParams.mBounds.set(task.mLastNonFullscreenBounds);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
index 103c3ab..f007149 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PendingRemoteAnimationRegistryTest.java
@@ -24,12 +24,13 @@
 import android.platform.test.annotations.Presubmit;
 import android.view.RemoteAnimationAdapter;
 
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.server.AnimationThread;
 import com.android.server.testutils.OffsettableClock;
 import com.android.server.testutils.TestHandler;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -41,7 +42,7 @@
  */
 @SmallTest
 @Presubmit
-public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
+public class PendingRemoteAnimationRegistryTest {
 
     @Mock RemoteAnimationAdapter mAdapter;
     private PendingRemoteAnimationRegistry mRegistry;
@@ -51,10 +52,15 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mService.mH.runWithScissors(() -> {
+        AnimationThread.getHandler().runWithScissors(() -> {
             mHandler = new TestHandler(null, mClock);
         }, 0);
-        mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+        mRegistry = new PendingRemoteAnimationRegistry(new WindowManagerGlobalLock(), mHandler);
+    }
+
+    @After
+    public void teadDown() {
+        AnimationThread.dispose();
     }
 
     @Test
@@ -74,7 +80,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 131005232)
     public void testTimeout() {
         mRegistry.addPendingAnimation("com.android.test", mAdapter);
         mClock.fastForward(5000);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 55a139a..79f808e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -421,6 +421,7 @@
     @Test
     public void testUsersTasks() {
         mRecentTasks.setOnlyTestVisibleRange();
+        mRecentTasks.unloadUserDataFromMemoryLocked(TEST_USER_0_ID);
 
         // Setup some tasks for the users
         mTaskPersister.mUserTaskIdsOverride = new SparseBooleanArray();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 9f092835..04d79ca 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -857,7 +857,7 @@
         // Assert that the stack is returned as expected.
         assertNotNull(result);
         assertEquals("The display ID of the stack should same as secondary display ",
-                secondaryDisplay.mDisplayId, result.mDisplayId);
+                secondaryDisplay.mDisplayId, result.getDisplayId());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index f90eca3..e507508 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -408,6 +408,16 @@
         }
     }
 
+    /**
+     * Throws if caller doesn't hold the given lock.
+     * @param lock the lock
+     */
+    static void checkHoldsLock(Object lock) {
+        if (!Thread.holdsLock(lock)) {
+            throw new IllegalStateException("Caller doesn't hold global lock.");
+        }
+    }
+
     protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
         // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
         // We keep the reference in order to prevent creating it twice.
@@ -433,7 +443,7 @@
             doReturn(aos).when(this).getAppOpsService();
             // Make sure permission checks aren't overridden.
             doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOperation(anyInt(), anyInt(),
-                    anyString(), nullable(String.class));
+                    anyString(), nullable(String.class), anyBoolean(), nullable(String.class));
 
             // UserManagerService
             final UserManagerService ums = mock(UserManagerService.class);
@@ -484,8 +494,8 @@
             spyOn(this);
 
             // Do not schedule idle that may touch methods outside the scope of the test.
-            doNothing().when(this).scheduleIdleLocked();
-            doNothing().when(this).scheduleIdleTimeoutLocked(any());
+            doNothing().when(this).scheduleIdle();
+            doNothing().when(this).scheduleIdleTimeout(any());
             // unit test version does not handle launch wake lock
             doNothing().when(this).acquireLaunchWakelock();
             doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 8e32dad..ca84932 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -84,7 +84,7 @@
 
         mTarget.finishTaskPositioning();
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
@@ -103,7 +103,7 @@
 
         mTarget.finishTaskPositioning(mWindow.mClient);
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
@@ -119,7 +119,7 @@
         assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
 
         mTarget.handleTapOutsideTask(content, 0, 0);
-        // Wait until the looper processes finishTaskPositioning.
+        // Wait until the looper processes handleTapOutsideTask.
         assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
 
         assertTrue(mTarget.isPositioningLocked());
@@ -127,7 +127,7 @@
 
         mTarget.finishTaskPositioning();
         // Wait until the looper processes finishTaskPositioning.
-        assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
+        assertTrue(waitHandlerIdle(mWm.mAnimationHandler, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
@@ -145,7 +145,7 @@
         mWindow.getTask().setResizeMode(RESIZE_MODE_UNRESIZEABLE);
 
         mTarget.handleTapOutsideTask(content, 0, 0);
-        // Wait until the looper processes finishTaskPositioning.
+        // Wait until the looper processes handleTapOutsideTask.
         assertTrue(waitHandlerIdle(mWm.mH, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 9f2bfa7..9562fa4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -345,6 +345,7 @@
         spyOn(parentWindowContainer);
         parentWindowContainer.setBounds(fullScreenBounds);
         doReturn(parentWindowContainer).when(task).getParent();
+        doReturn(stack).when(task).getStack();
         doReturn(true).when(parentWindowContainer).handlesOrientationChangeFromDescendant();
 
         // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 6c3410a..b4f5751 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -110,12 +110,12 @@
     public void testStackRemoveImmediately() {
         final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
-        assertEquals(stack, task.getTaskStack());
+        assertEquals(stack, task.getStack());
 
         // Remove stack and check if its child is also removed.
         stack.removeImmediately();
         assertNull(stack.getDisplayContent());
-        assertNull(task.getTaskStack());
+        assertNull(task.getStack());
     }
 
     @Test
@@ -131,7 +131,7 @@
         assertEquals(0, stack.getChildCount());
         assertNull(stack.getDisplayContent());
         assertNull(task.getDisplayContent());
-        assertNull(task.getTaskStack());
+        assertNull(task.getStack());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index e5121b9..77af5ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -72,7 +72,7 @@
         private final DisplayInfo mInfo;
         private boolean mCanRotate = true;
         private int mWindowingMode = WINDOWING_MODE_FULLSCREEN;
-        private int mPosition = POSITION_TOP;
+        private int mPosition = POSITION_BOTTOM;
         private final ActivityTaskManagerService mService;
         private boolean mSystemDecorations = false;
 
@@ -127,13 +127,13 @@
             return this;
         }
         TestDisplayContent build() {
+            SystemServicesTestRule.checkHoldsLock(mService.mGlobalLock);
+
             final int displayId = SystemServicesTestRule.sNextDisplayId++;
             final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
                     mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-            final TestDisplayContent newDisplay;
-            synchronized (mService.mGlobalLock) {
-                newDisplay = new TestDisplayContent(mService.mStackSupervisor, display);
-            }
+            final TestDisplayContent newDisplay =
+                    new TestDisplayContent(mService.mStackSupervisor, display);
             // disable the normal system decorations
             final DisplayPolicy displayPolicy = newDisplay.mDisplayContent.getDisplayPolicy();
             spyOn(displayPolicy);
@@ -153,6 +153,10 @@
                 doReturn(false).when(newDisplay.mDisplayContent)
                         .handlesOrientationChangeFromDescendant();
             }
+            // Please add stubbing before this line. Services will start using this display in other
+            // threads immediately after adding it to hierarchy. Calling doAnswer() type of stubbing
+            // reduces chance of races, but still doesn't eliminate race conditions.
+            mService.mRootWindowContainer.addChild(newDisplay, mPosition);
             return newDisplay;
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 634d2f0..08ee0eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -80,11 +80,6 @@
     }
 
     @Override
-    public boolean checkShowToOwnerOnly(WindowManager.LayoutParams attrs) {
-        return false;
-    }
-
-    @Override
     public void adjustConfigurationLw(Configuration config, int keyboardPresence,
             int navigationPresence) {
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 248b06b..d3cd3cb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -21,6 +21,7 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyFloat;
@@ -49,7 +50,12 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
+import android.view.RemoteAnimationAdapter;
+import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
@@ -782,6 +788,63 @@
         assertEquals(newDc, activity.mDisplayContent);
     }
 
+    @Test
+    public void testTaskCanApplyAnimation() {
+        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityRecord activity =
+                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        verifyWindowContainerApplyAnimation(task, activity);
+    }
+
+    @Test
+    public void testStackCanApplyAnimation() {
+        final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
+        final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(mDisplayContent,
+                createTaskInStack(stack, 0 /* userId */));
+        verifyWindowContainerApplyAnimation(stack, activity);
+    }
+
+    private void verifyWindowContainerApplyAnimation(WindowContainer wc, ActivityRecord act) {
+        // Initial remote animation for app transition.
+        final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+                new IRemoteAnimationRunner.Stub() {
+                    @Override
+                    public void onAnimationStart(RemoteAnimationTarget[] apps,
+                            RemoteAnimationTarget[] wallpapers,
+                            IRemoteAnimationFinishedCallback finishedCallback) {
+                        try {
+                            finishedCallback.onAnimationFinished();
+                        } catch (RemoteException e) {
+                            e.printStackTrace();
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationCancelled() {
+                    }
+                }, 0, 0, false);
+        adapter.setCallingPidUid(123, 456);
+        wc.getDisplayContent().prepareAppTransition(TRANSIT_TASK_OPEN, false);
+        wc.getDisplayContent().mAppTransition.overridePendingAppTransitionRemote(adapter);
+        spyOn(wc);
+        doReturn(true).when(wc).okToAnimate();
+
+        // Make sure animating state is as expected after applied animation.
+        assertTrue(wc.applyAnimation(null, TRANSIT_TASK_OPEN, true, false));
+        assertEquals(wc.getTopMostActivity(), act);
+        assertTrue(wc.isAnimating());
+        assertTrue(act.isAnimating(PARENTS));
+
+        // Make sure animation finish callback will be received and reset animating state after
+        // animation finish.
+        wc.getDisplayContent().mAppTransition.goodToGo(TRANSIT_TASK_OPEN, act,
+                mDisplayContent.mOpeningApps);
+        verify(wc).onAnimationFinished();
+        assertFalse(wc.isAnimating());
+        assertFalse(act.isAnimating(PARENTS));
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 6d23b2e..e081ca3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -421,8 +421,10 @@
                 .setWindow(statusBar, null /* frameProvider */);
         mDisplayContent.getInsetsStateController().onBarControlTargetChanged(
                 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */);
+        final InsetsSource source = new InsetsSource(ITYPE_STATUS_BAR);
+        source.setVisible(false);
         mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
-                .onInsetsModified(app, new InsetsSource(ITYPE_STATUS_BAR));
+                .onInsetsModified(app, source);
         waitUntilHandlersIdle();
         assertFalse(statusBar.isVisible());
     }
@@ -522,7 +524,8 @@
     public void testVisibilityChangeSwitchUser() {
         final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
         window.mHasSurface = true;
-        window.setShowToOwnerOnlyLocked(true);
+        spyOn(window);
+        doReturn(false).when(window).showForAllUsers();
 
         mWm.mCurrentUserId = 1;
         window.switchUser(mWm.mCurrentUserId);
diff --git a/services/usage/OWNERS b/services/usage/OWNERS
new file mode 100644
index 0000000..9daa093
--- /dev/null
+++ b/services/usage/OWNERS
@@ -0,0 +1,4 @@
+mwachens@google.com
+varunshah@google.com
+huiyu@google.com
+yamasani@google.com
diff --git a/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
new file mode 100644
index 0000000..b75510b
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/EventSequenceValidator.java
@@ -0,0 +1,255 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+
+/**
+ * A validator to check the correctness of event sequence during app startup.
+ *
+ * <p> A valid state transition of event sequence is shown as the following:
+ *
+ * <pre>
+ *
+ *                                +--------------------+
+ *                                |                    |
+ *                                |        INIT        |
+ *                                |                    |
+ *                                +--------------------+
+ *                                          |
+ *                                          |
+ *                                          ↓
+ *                                +--------------------+
+ *                                |                    |
+ *            +-------------------|   INTENT_STARTED   | ←--------------------------------+
+ *            |                   |                    |                                  |
+ *            |                   +--------------------+                                  |
+ *            |                             |                                             |
+ *            |                             |                                             |
+ *            ↓                             ↓                                             |
+ * +--------------------+         +--------------------+                                  |
+ * |                    |         |                    |                                  |
+ * |   INTENT_FAILED    |         | ACTIVITY_LAUNCHED  |------------------+               |
+ * |                    |         |                    |                  |               |
+ * +--------------------+         +--------------------+                  |               |
+ *            |                              |                            |               |
+ *            |                              ↓                            ↓               |
+ *            |                   +--------------------+       +--------------------+     |
+ *            |                   |                    |       |                    |     |
+ *            +------------------ |  ACTIVITY_FINISHED |       | ACTIVITY_CANCELLED |     |
+ *            |                   |                    |       |                    |     |
+ *            |                   +--------------------+       +--------------------+     |
+ *            |                              |                            |               |
+ *            |                              |                            |               |
+ *            |                              ↓                            |               |
+ *            |                   +--------------------+                  |               |
+ *            |                   |                    |                  |               |
+ *            |                   | REPORT_FULLY_DRAWN |                  |               |
+ *            |                   |                    |                  |               |
+ *            |                   +--------------------+                  |               |
+ *            |                              |                            |               |
+ *            |                              |                            |               |
+ *            |                              ↓                            |               |
+ *            |                   +--------------------+                  |               |
+ *            |                   |                    |                  |               |
+ *            +-----------------→ |        END         |←-----------------+               |
+ *                                |                    |                                  |
+ *                                +--------------------+                                  |
+ *                                           |                                            |
+ *                                           |                                            |
+ *                                           |                                            |
+ *                                           +---------------------------------------------
+ *
+ * <p> END is not a real state in implementation. All states that points to END directly
+ * could transition to INTENT_STARTED.
+ *
+ * <p> If any bad transition happened, the state becomse UNKNOWN. The UNKNOWN state
+ * could be * accumulated, because during the UNKNOWN state more IntentStarted may
+ * be triggered. To recover from UNKNOWN to INIT, all the accumualted IntentStarted
+ * should termniate.
+ *
+ * <p> During UNKNOWN state, each IntentStarted increases the accumulation, and any of
+ * IntentFailed, ActivityLaunchCancelled and ActivityFinished decreases the accumulation.
+ * ReportFullyDrawn doesn't impact the accumulation.
+ */
+public class EventSequenceValidator implements ActivityMetricsLaunchObserver {
+  static final String TAG = "EventSequenceValidator";
+
+  private State state = State.INIT;
+  private long accIntentStartedEvents = 0;
+
+  @Override
+  public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
+    if (state == State.UNKNOWN) {
+      Log.e(TAG, "IntentStarted during UNKNOWN." + intent);
+      incAccIntentStartedEvents();
+      return;
+    }
+
+    if (state != State.INIT &&
+        state != State.INTENT_FAILED &&
+        state != State.ACTIVITY_CANCELLED &&
+        state != State.ACTIVITY_FINISHED &&
+        state != State.REPORT_FULLY_DRAWN) {
+      Log.e(TAG,
+          String.format("Cannot transition from %s to %s", state, State.INTENT_STARTED));
+      incAccIntentStartedEvents();
+      incAccIntentStartedEvents();
+      return;
+    }
+
+    Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_STARTED));
+    state = State.INTENT_STARTED;
+  }
+
+  @Override
+  public void onIntentFailed() {
+    if (state == State.UNKNOWN) {
+      Log.e(TAG, "IntentFailed during UNKNOWN.");
+      decAccIntentStartedEvents();
+      return;
+    }
+    if (state != State.INTENT_STARTED) {
+      Log.e(TAG,
+          String.format("Cannot transition from %s to %s", state, State.INTENT_FAILED));
+      incAccIntentStartedEvents();
+      return;
+    }
+
+    Log.i(TAG, String.format("Tansition from %s to %s", state, State.INTENT_FAILED));
+    state = State.INTENT_FAILED;
+  }
+
+  @Override
+  public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+      @Temperature int temperature) {
+    if (state == State.UNKNOWN) {
+      Log.e(TAG, "onActivityLaunched during UNKNOWN.");
+      return;
+    }
+    if (state != State.INTENT_STARTED) {
+      Log.e(TAG,
+          String.format("Cannot transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
+      incAccIntentStartedEvents();
+      return;
+    }
+
+    Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_LAUNCHED));
+    state = State.ACTIVITY_LAUNCHED;
+  }
+
+  @Override
+  public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+    if (state == State.UNKNOWN) {
+      Log.e(TAG, "onActivityLaunchCancelled during UNKNOWN.");
+      decAccIntentStartedEvents();
+      return;
+    }
+    if (state != State.ACTIVITY_LAUNCHED) {
+      Log.e(TAG,
+          String.format("Cannot transition from %s to %s", state, State.ACTIVITY_CANCELLED));
+      incAccIntentStartedEvents();
+      return;
+    }
+
+    Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_CANCELLED));
+    state = State.ACTIVITY_CANCELLED;
+  }
+
+  @Override
+  public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+      long timestampNs) {
+    if (state == State.UNKNOWN) {
+      Log.e(TAG, "onActivityLaunchFinished during UNKNOWN.");
+      decAccIntentStartedEvents();
+      return;
+    }
+
+    if (state != State.ACTIVITY_LAUNCHED) {
+      Log.e(TAG,
+          String.format("Cannot transition from %s to %s", state, State.ACTIVITY_FINISHED));
+      incAccIntentStartedEvents();
+      return;
+    }
+
+    Log.i(TAG, String.format("Transition from %s to %s", state, State.ACTIVITY_FINISHED));
+    state = State.ACTIVITY_FINISHED;
+  }
+
+  @Override
+  public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+      long timestampNs) {
+    if (state == State.UNKNOWN) {
+      Log.e(TAG, "onReportFullyDrawn during UNKNOWN.");
+      return;
+    }
+    if (state == State.INIT) {
+      return;
+    }
+
+    if (state != State.ACTIVITY_FINISHED) {
+      Log.e(TAG,
+          String.format("Cannot transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
+      return;
+    }
+
+    Log.i(TAG, String.format("Transition from %s to %s", state, State.REPORT_FULLY_DRAWN));
+    state = State.REPORT_FULLY_DRAWN;
+  }
+
+  enum State {
+    INIT,
+    INTENT_STARTED,
+    INTENT_FAILED,
+    ACTIVITY_LAUNCHED,
+    ACTIVITY_CANCELLED,
+    ACTIVITY_FINISHED,
+    REPORT_FULLY_DRAWN,
+    UNKNOWN,
+  }
+
+  private void incAccIntentStartedEvents() {
+    if (accIntentStartedEvents < 0) {
+      throw new AssertionError(
+          String.format("The number of unknows cannot be negative"));
+    }
+    if (accIntentStartedEvents == 0) {
+      state = State.UNKNOWN;
+    }
+    ++accIntentStartedEvents;
+    Log.i(TAG,
+        String.format("inc AccIntentStartedEvents to %d", accIntentStartedEvents));
+  }
+
+  private void decAccIntentStartedEvents() {
+    if (accIntentStartedEvents <= 0) {
+      throw new AssertionError(
+          String.format("The number of unknows cannot be negative"));
+    }
+    if(accIntentStartedEvents == 1) {
+      state = State.INIT;
+    }
+    --accIntentStartedEvents;
+    Log.i(TAG,
+        String.format("dec AccIntentStartedEvents to %d", accIntentStartedEvents));
+  }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index f753548..badff7b 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -283,6 +283,7 @@
     }
 
     private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver();
+    private final EventSequenceValidator mEventSequenceValidator = new EventSequenceValidator();
     private boolean mRegisteredListeners = false;
 
     private void registerInProcessListenersLocked() {
@@ -303,6 +304,7 @@
         ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
                 provideLaunchObserverRegistry();
         launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
+        launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
 
         mRegisteredListeners = true;
     }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 0becaf2..8808339 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -502,51 +502,116 @@
     //**********************************************************************************************
 
     /**
-     * Define IMS Audio Codec
+     * Indicates that the audio codec is currently not specified or is unknown.
      */
-    // Current audio codec is NONE
     public static final int AUDIO_CODEC_NONE = ImsStreamMediaProfile.AUDIO_QUALITY_NONE; // 0
-    // Current audio codec is AMR
+    /**
+     * Adaptive Multi-rate audio codec.
+     */
     public static final int AUDIO_CODEC_AMR = ImsStreamMediaProfile.AUDIO_QUALITY_AMR; // 1
-    // Current audio codec is AMR_WB
+    /**
+     * Adaptive Multi-rate wideband audio codec.
+     */
     public static final int AUDIO_CODEC_AMR_WB = ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB; // 2
-    // Current audio codec is QCELP13K
+    /**
+     * Qualcomm code-excited linear prediction 13 kilobit audio codec.
+     */
     public static final int AUDIO_CODEC_QCELP13K = ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K; //3
-    // Current audio codec is EVRC
+    /**
+     * Enhanced Variable Rate Codec.  See 3GPP2 C.S0014-A.
+     */
     public static final int AUDIO_CODEC_EVRC = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC; // 4
-    // Current audio codec is EVRC_B
+    /**
+     * Enhanced Variable Rate Codec B.  Commonly used on CDMA networks.
+     */
     public static final int AUDIO_CODEC_EVRC_B = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B; // 5
-    // Current audio codec is EVRC_WB
+    /**
+     * Enhanced Variable Rate Wideband Codec.  See RFC5188.
+     */
     public static final int AUDIO_CODEC_EVRC_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB; // 6
-    // Current audio codec is EVRC_NW
+    /**
+     * Enhanced Variable Rate Narrowband-Wideband Codec.
+     */
     public static final int AUDIO_CODEC_EVRC_NW = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW; // 7
-    // Current audio codec is GSM_EFR
+    /**
+     * GSM Enhanced Full-Rate audio codec, also known as GSM-EFR, GSM 06.60, or simply EFR.
+     */
     public static final int AUDIO_CODEC_GSM_EFR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR; // 8
-    // Current audio codec is GSM_FR
+    /**
+     * GSM Full-Rate audio codec, also known as GSM-FR, GSM 06.10, GSM, or simply FR.
+     */
     public static final int AUDIO_CODEC_GSM_FR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR; // 9
-    // Current audio codec is GSM_HR
+    /**
+     * GSM Half Rate audio codec.
+     */
     public static final int AUDIO_CODEC_GSM_HR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR; // 10
-    // Current audio codec is G711U
+    /**
+     * ITU-T G711U audio codec.
+     */
     public static final int AUDIO_CODEC_G711U = ImsStreamMediaProfile.AUDIO_QUALITY_G711U; // 11
-    // Current audio codec is G723
+    /**
+     * ITU-T G723 audio codec.
+     */
     public static final int AUDIO_CODEC_G723 = ImsStreamMediaProfile.AUDIO_QUALITY_G723; // 12
-    // Current audio codec is G711A
+    /**
+     * ITU-T G711A audio codec.
+     */
     public static final int AUDIO_CODEC_G711A = ImsStreamMediaProfile.AUDIO_QUALITY_G711A; // 13
-    // Current audio codec is G722
+    /**
+     * ITU-T G722 audio codec.
+     */
     public static final int AUDIO_CODEC_G722 = ImsStreamMediaProfile.AUDIO_QUALITY_G722; // 14
-    // Current audio codec is G711AB
+    /**
+     * ITU-T G711AB audio codec.
+     */
     public static final int AUDIO_CODEC_G711AB = ImsStreamMediaProfile.AUDIO_QUALITY_G711AB; // 15
-    // Current audio codec is G729
+    /**
+     * ITU-T G729 audio codec.
+     */
     public static final int AUDIO_CODEC_G729 = ImsStreamMediaProfile.AUDIO_QUALITY_G729; // 16
-    // Current audio codec is EVS_NB
+    /**
+     * Enhanced Voice Services Narrowband audio codec.  See 3GPP TS 26.441.
+     */
     public static final int AUDIO_CODEC_EVS_NB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB; // 17
-    // Current audio codec is EVS_WB
+    /**
+     * Enhanced Voice Services Wideband audio codec.  See 3GPP TS 26.441.
+     */
     public static final int AUDIO_CODEC_EVS_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB; // 18
-    // Current audio codec is EVS_SWB
+    /**
+     * Enhanced Voice Services Super-Wideband audio codec.  See 3GPP TS 26.441.
+     */
     public static final int AUDIO_CODEC_EVS_SWB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB; // 19
-    // Current audio codec is EVS_FB
+    /**
+     * Enhanced Voice Services Fullband audio codec.  See 3GPP TS 26.441.
+     */
     public static final int AUDIO_CODEC_EVS_FB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB; // 20
 
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "AUDIO_CODEC_", value = {
+            AUDIO_CODEC_NONE,
+            AUDIO_CODEC_AMR,
+            AUDIO_CODEC_AMR_WB,
+            AUDIO_CODEC_QCELP13K,
+            AUDIO_CODEC_EVRC,
+            AUDIO_CODEC_EVRC_B,
+            AUDIO_CODEC_EVRC_WB,
+            AUDIO_CODEC_EVRC_NW,
+            AUDIO_CODEC_GSM_EFR,
+            AUDIO_CODEC_GSM_FR,
+            AUDIO_CODEC_GSM_HR,
+            AUDIO_CODEC_G711U,
+            AUDIO_CODEC_G723,
+            AUDIO_CODEC_G711A,
+            AUDIO_CODEC_G722,
+            AUDIO_CODEC_G711AB,
+            AUDIO_CODEC_G729,
+            AUDIO_CODEC_EVS_NB,
+            AUDIO_CODEC_EVS_SWB,
+            AUDIO_CODEC_EVS_FB
+    })
+    public @interface AudioCodec {}
+
     /**
      * Connection extra key used to store the last forwarded number associated with the current
      * connection.  Used to communicate to the user interface that the connection was forwarded via
@@ -640,10 +705,10 @@
             "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
 
     /**
-     * The audio codec in use for the current {@link Connection}, if known. Valid values include
-     * {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}.
+     * The audio codec in use for the current {@link Connection}, if known.  Examples of valid
+     * values include {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}.
      */
-    public static final String EXTRA_AUDIO_CODEC =
+    public static final @AudioCodec String EXTRA_AUDIO_CODEC =
             "android.telecom.extra.AUDIO_CODEC";
 
     /**
diff --git a/telephony/OWNERS b/telephony/OWNERS
index 2a6e8de..58a7ea0 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -14,4 +14,5 @@
 refuhoo@google.com
 paulye@google.com
 nazaninb@google.com
-sarahchin@google.com
\ No newline at end of file
+sarahchin@google.com
+dbright@google.com
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
similarity index 97%
rename from telephony/java/android/telephony/LocationAccessPolicy.java
rename to telephony/common/android/telephony/LocationAccessPolicy.java
index 95aa101..f39981f 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 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.
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package android.telephony;
@@ -60,6 +60,7 @@
         DENIED_HARD,
     }
 
+    /** Data structure for location permission query */
     public static class LocationPermissionQuery {
         public final String callingPackage;
         public final String callingFeatureId;
@@ -83,6 +84,7 @@
             this.method = method;
         }
 
+        /** Builder for LocationPermissionQuery */
         public static class Builder {
             private String mCallingPackage;
             private String mCallingFeatureId;
@@ -161,6 +163,7 @@
                 return this;
             }
 
+            /** build LocationPermissionQuery */
             public LocationPermissionQuery build() {
                 return new LocationPermissionQuery(mCallingPackage, mCallingFeatureId,
                         mCallingUid, mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine,
@@ -258,6 +261,7 @@
         }
     }
 
+    /** Check if location permissions have been granted */
     public static LocationPermissionResult checkLocationPermission(
             Context context, LocationPermissionQuery query) {
         // Always allow the phone process and system server to access location. This avoid
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
similarity index 94%
rename from telephony/java/com/android/internal/telephony/CarrierAppUtils.java
rename to telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 2dc6ae3..eb02ea6f 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 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.
@@ -11,7 +11,7 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.internal.telephony;
@@ -74,7 +74,7 @@
      * system startup prior to any application running, as well as any time the set of carrier
      * privileged apps may have changed.
      */
-    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+    public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
             IPackageManager packageManager, IPermissionManager permissionManager,
             TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) {
         if (DEBUG) {
@@ -101,7 +101,7 @@
      * broadcasts. The app will continue to run (briefly) after being disabled, before the Package
      * Manager can kill it, and this can lead to crashes as the app is in an unexpected state.
      */
-    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+    public static synchronized void disableCarrierAppsUntilPrivileged(String callingPackage,
             IPackageManager packageManager, IPermissionManager permissionManager,
             ContentResolver contentResolver, int userId) {
         if (DEBUG) {
@@ -119,7 +119,10 @@
                 systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed);
     }
 
-    // Must be public b/c framework unit tests can't access package-private methods.
+    /**
+     * Disable carrier apps until they are privileged
+     * Must be public b/c framework unit tests can't access package-private methods.
+     */
     @VisibleForTesting
     public static void disableCarrierAppsUntilPrivileged(String callingPackage,
             IPackageManager packageManager, IPermissionManager permissionManager,
@@ -169,10 +172,10 @@
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
                     if (!ai.isUpdatedSystemApp()
-                            && (ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                            || ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            && (ai.enabledSetting
+                            == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            || ai.enabledSetting
+                            == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
                             || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
                         Rlog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
                                 + userId);
@@ -191,10 +194,10 @@
                     // Also enable any associated apps for this carrier app.
                     if (associatedAppList != null) {
                         for (ApplicationInfo associatedApp : associatedAppList) {
-                            if (associatedApp.enabledSetting ==
-                                    PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
-                                    || associatedApp.enabledSetting ==
-                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            if (associatedApp.enabledSetting
+                                    == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                                    || associatedApp.enabledSetting
+                                    == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
                                     || (associatedApp.flags
                                     & ApplicationInfo.FLAG_INSTALLED) == 0) {
                                 Rlog.i(TAG, "Update associated state(" + associatedApp.packageName
@@ -219,8 +222,8 @@
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
                     if (!ai.isUpdatedSystemApp()
-                            && ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            && ai.enabledSetting
+                            == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
                         Rlog.i(TAG, "Update state(" + packageName
                                 + "): DISABLED_UNTIL_USED for user " + userId);
@@ -294,8 +297,8 @@
             ApplicationInfo ai = candidates.get(i);
             String packageName = ai.packageName;
             boolean hasPrivileges =
-                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
-                            TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+                            == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
             if (!hasPrivileges) {
                 candidates.remove(i);
             }
diff --git a/telephony/common/com/android/internal/telephony/EncodeException.java b/telephony/common/com/android/internal/telephony/EncodeException.java
index cdc853e..bb723a0 100644
--- a/telephony/common/com/android/internal/telephony/EncodeException.java
+++ b/telephony/common/com/android/internal/telephony/EncodeException.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * {@hide}
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
index 922af12..0b47547 100644
--- a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
+++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
@@ -24,17 +24,17 @@
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.UserHandle;
 
-import com.android.internal.os.BackgroundThread;
-
 /**
  * Helper class for monitoring the state of packages: adding, removing,
  * updating, and disappearing and reappearing on the SD card.
  */
 public abstract class PackageChangeReceiver extends BroadcastReceiver {
     static final IntentFilter sPackageIntentFilter = new IntentFilter();
+    private static HandlerThread sHandlerThread;
     static {
         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -43,28 +43,24 @@
         sPackageIntentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         sPackageIntentFilter.addDataScheme("package");
     }
+    // Keep an instance of Context around as long as we still want the receiver:
+    // if the instance of Context gets garbage-collected, it'll unregister the receiver, so only
+    // unset when we want to unregister.
     Context mRegisteredContext;
 
     /**
-     * To register the intents that needed for monitoring the state of packages
+     * To register the intents that needed for monitoring the state of packages. Once this method
+     * has been called on an instance of {@link PackageChangeReceiver}, all subsequent calls must
+     * have the same {@code user} argument.
      */
     public void register(@NonNull Context context, @Nullable Looper thread,
             @Nullable UserHandle user) {
         if (mRegisteredContext != null) {
             throw new IllegalStateException("Already registered");
         }
-        Handler handler = (thread == null) ? BackgroundThread.getHandler() : new Handler(thread);
-        mRegisteredContext = context;
-        if (handler != null) {
-            if (user != null) {
-                context.registerReceiverAsUser(this, user, sPackageIntentFilter, null, handler);
-            } else {
-                context.registerReceiver(this, sPackageIntentFilter,
-                        null, handler);
-            }
-        } else {
-            throw new NullPointerException();
-        }
+        Handler handler = new Handler(thread == null ? getStaticLooper() : thread);
+        mRegisteredContext = user == null ? context : context.createContextAsUser(user, 0);
+        mRegisteredContext.registerReceiver(this, sPackageIntentFilter, null, handler);
     }
 
     /**
@@ -78,6 +74,14 @@
         mRegisteredContext = null;
     }
 
+    private static synchronized Looper getStaticLooper() {
+        if (sHandlerThread == null) {
+            sHandlerThread = new HandlerThread(PackageChangeReceiver.class.getSimpleName());
+            sHandlerThread.start();
+        }
+        return sHandlerThread.getLooper();
+    }
+
     /**
      * This method is invoked when receive the Intent.ACTION_PACKAGE_ADDED
      */
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index 70ce1bf..b302589 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -44,6 +44,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
@@ -57,6 +58,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
 /**
  * Class for managing the primary application that we will deliver SMS/MMS messages to
@@ -65,10 +67,10 @@
  */
 public final class SmsApplication {
     static final String LOG_TAG = "SmsApplication";
-    private static final String PHONE_PACKAGE_NAME = "com.android.phone";
-    private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
-    private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
-    private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
+    public static final String PHONE_PACKAGE_NAME = "com.android.phone";
+    public static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
+    public static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
+    public static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
 
     private static final String SCHEME_SMS = "sms";
     private static final String SCHEME_SMSTO = "smsto";
@@ -77,13 +79,13 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_MULTIUSER = false;
 
-    private static final int[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
-            AppOpsManager.OP_READ_SMS,
-            AppOpsManager.OP_WRITE_SMS,
-            AppOpsManager.OP_RECEIVE_SMS,
-            AppOpsManager.OP_RECEIVE_WAP_PUSH,
-            AppOpsManager.OP_SEND_SMS,
-            AppOpsManager.OP_READ_CELL_BROADCASTS
+    private static final String[] DEFAULT_APP_EXCLUSIVE_APPOPS = {
+            AppOpsManager.OPSTR_READ_SMS,
+            AppOpsManager.OPSTR_WRITE_SMS,
+            AppOpsManager.OPSTR_RECEIVE_SMS,
+            AppOpsManager.OPSTR_RECEIVE_WAP_PUSH,
+            AppOpsManager.OPSTR_SEND_SMS,
+            AppOpsManager.OPSTR_READ_CELL_BROADCASTS
     };
 
     private static SmsPackageMonitor sSmsPackageMonitor = null;
@@ -247,6 +249,7 @@
     private static Collection<SmsApplicationData> getApplicationCollectionInternal(
             Context context, int userId) {
         PackageManager packageManager = context.getPackageManager();
+        UserHandle userHandle = UserHandle.of(userId);
 
         // Get the list of apps registered for SMS
         Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
@@ -255,7 +258,7 @@
         }
         List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
                 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                userId);
+                userHandle);
 
         HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
 
@@ -282,7 +285,7 @@
         intent.setDataAndType(null, "application/vnd.wap.mms-message");
         List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
                 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                userId);
+                userHandle);
         for (ResolveInfo resolveInfo : mmsReceivers) {
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
             if (activityInfo == null) {
@@ -324,7 +327,7 @@
                 Uri.fromParts(SCHEME_SMSTO, "", null));
         List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent,
                 PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                userId);
+                userHandle);
         for (ResolveInfo resolveInfo : sendToActivities) {
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
             if (activityInfo == null) {
@@ -342,7 +345,7 @@
         List<ResolveInfo> smsAppChangedReceivers =
                 packageManager.queryBroadcastReceiversAsUser(intent,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
                     smsAppChangedReceivers);
@@ -369,7 +372,7 @@
         List<ResolveInfo> providerChangedReceivers =
                 packageManager.queryBroadcastReceiversAsUser(intent,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
                     providerChangedReceivers);
@@ -396,7 +399,7 @@
         List<ResolveInfo> simFullReceivers =
                 packageManager.queryBroadcastReceiversAsUser(intent,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userHandle);
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getApplicationCollectionInternal simFullReceivers="
                     + simFullReceivers);
@@ -496,7 +499,7 @@
 
         // If we found a package, make sure AppOps permissions are set up correctly
         if (applicationData != null) {
-            // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
+            // We can only call unsafeCheckOp if we are privileged (updateIfNeeded) or if the app we
             // are checking is for our current uid. Doing this check from the unprivileged current
             // SMS app allows us to tell the current SMS app that it is not in a good state and
             // needs to ask to be the current SMS app again to work properly.
@@ -550,23 +553,23 @@
         // apps, all of them should be able to write to telephony provider.
         // This is to allow the proxy package permission check in telephony provider
         // to pass.
-        for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
-            appOps.setUidMode(appop, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
+        for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+            appOps.setUidMode(opStr, Process.PHONE_UID, AppOpsManager.MODE_ALLOWED);
         }
     }
 
     private static boolean tryFixExclusiveSmsAppops(Context context,
             SmsApplicationData applicationData, boolean updateIfNeeded) {
         AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
-        for (int appOp : DEFAULT_APP_EXCLUSIVE_APPOPS) {
-            int mode = appOps.checkOp(appOp, applicationData.mUid,
+        for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+            int mode = appOps.unsafeCheckOp(opStr, applicationData.mUid,
                     applicationData.mPackageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 Rlog.e(LOG_TAG, applicationData.mPackageName + " lost "
-                        + AppOpsManager.modeToName(appOp) + ": "
+                        + opStr + ": "
                         + (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
                 if (updateIfNeeded) {
-                    appOps.setUidMode(appOp, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
+                    appOps.setUidMode(opStr, applicationData.mUid, AppOpsManager.MODE_ALLOWED);
                 } else {
                     return false;
                 }
@@ -625,7 +628,8 @@
         }
 
         // We only make the change if the new package is valid
-        PackageManager packageManager = context.getPackageManager();
+        PackageManager packageManager =
+                context.createContextAsUser(userHandle, 0).getPackageManager();
         Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
                 context, userId);
         SmsApplicationData oldAppData = oldPackageName != null ?
@@ -636,8 +640,7 @@
             AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
             if (oldPackageName != null) {
                 try {
-                    int uid = packageManager.getPackageInfoAsUser(
-                            oldPackageName, 0, userId).applicationInfo.uid;
+                    int uid = packageManager.getPackageInfo(oldPackageName, 0).applicationInfo.uid;
                     setExclusiveAppops(oldPackageName, appOps, uid, AppOpsManager.MODE_DEFAULT);
                 } catch (NameNotFoundException e) {
                     Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
@@ -752,7 +755,7 @@
         }
         try {
             PackageInfo info = packageManager.getPackageInfo(packageName, 0);
-            int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+            int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid,
                     packageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS:  (fixing)");
@@ -768,8 +771,8 @@
 
     private static void setExclusiveAppops(String pkg, AppOpsManager appOpsManager, int uid,
             int mode) {
-        for (int appop : DEFAULT_APP_EXCLUSIVE_APPOPS) {
-            appOpsManager.setUidMode(appop, uid, mode);
+        for (String opStr : DEFAULT_APP_EXCLUSIVE_APPOPS) {
+            appOpsManager.setUidMode(opStr, uid, mode);
         }
     }
 
@@ -801,9 +804,16 @@
         }
 
         private void onPackageChanged() {
-            PackageManager packageManager = mContext.getPackageManager();
+            int userId;
+            try {
+                userId = getSendingUser().getIdentifier();
+            } catch (NullPointerException e) {
+                // This should never happen in prod -- unit tests will put the receiver into a
+                // unusual state where the pending result is null, which produces a NPE when calling
+                // getSendingUserId. Just pretend like it's the system user for testing.
+                userId = UserHandle.USER_SYSTEM;
+            }
             Context userContext = mContext;
-            final int userId = getSendingUserId();
             if (userId != UserHandle.USER_SYSTEM) {
                 try {
                     userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
@@ -814,10 +824,11 @@
                     }
                 }
             }
+            PackageManager packageManager = userContext.getPackageManager();
             // Ensure this component is still configured as the preferred activity
             ComponentName componentName = getDefaultSendToApplication(userContext, true);
             if (componentName != null) {
-                configurePreferredActivity(packageManager, componentName, userId);
+                configurePreferredActivity(packageManager, componentName);
             }
         }
     }
@@ -829,41 +840,36 @@
 
     @UnsupportedAppUsage
     private static void configurePreferredActivity(PackageManager packageManager,
-            ComponentName componentName, int userId) {
+            ComponentName componentName) {
         // Add the four activity preferences we want to direct to this app.
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS);
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO);
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS);
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO);
+        replacePreferredActivity(packageManager, componentName, SCHEME_SMS);
+        replacePreferredActivity(packageManager, componentName, SCHEME_SMSTO);
+        replacePreferredActivity(packageManager, componentName, SCHEME_MMS);
+        replacePreferredActivity(packageManager, componentName, SCHEME_MMSTO);
     }
 
     /**
      * Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
      */
     private static void replacePreferredActivity(PackageManager packageManager,
-            ComponentName componentName, int userId, String scheme) {
+            ComponentName componentName, String scheme) {
         // Build the set of existing activities that handle this scheme
         Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
-        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser(
-                intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER,
-                userId);
+        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(
+                intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER);
 
-        // Build the set of ComponentNames for these activities
-        final int n = resolveInfoList.size();
-        ComponentName[] set = new ComponentName[n];
-        for (int i = 0; i < n; i++) {
-            ResolveInfo info = resolveInfoList.get(i);
-            set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
-        }
+        List<ComponentName> components = resolveInfoList.stream().map(info ->
+                new ComponentName(info.activityInfo.packageName, info.activityInfo.name))
+                .collect(Collectors.toList());
 
         // Update the preferred SENDTO activity for the specified scheme
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_SENDTO);
         intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
         intentFilter.addDataScheme(scheme);
-        packageManager.replacePreferredActivityAsUser(intentFilter,
+        packageManager.replacePreferredActivity(intentFilter,
                 IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
-                set, componentName, userId);
+                components, componentName);
     }
 
     /**
@@ -894,6 +900,7 @@
      * @param userId target user ID.
      * @return component name of the app and class to deliver SMS messages to
      */
+    @VisibleForTesting
     public static ComponentName getDefaultSmsApplicationAsUser(Context context,
             boolean updateIfNeeded, int userId) {
         final long token = Binder.clearCallingIdentity();
diff --git a/telephony/java/com/android/internal/telephony/util/ArrayUtils.java b/telephony/common/com/android/internal/telephony/util/ArrayUtils.java
similarity index 86%
rename from telephony/java/com/android/internal/telephony/util/ArrayUtils.java
rename to telephony/common/com/android/internal/telephony/util/ArrayUtils.java
index 2905125..28401a6 100644
--- a/telephony/java/com/android/internal/telephony/util/ArrayUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/ArrayUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -29,17 +29,30 @@
 
     /**
      * Adds value to given array if not already present, providing set-like behavior.
+     *
+     * @param kind    The class of the array elements.
+     * @param array   The array to append to.
+     * @param element The array element to append.
+     * @return The array containing the appended element.
      */
     @SuppressWarnings("unchecked")
-    public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
+    @NonNull
+    public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
         return appendElement(kind, array, element, false);
     }
 
     /**
      * Adds value to given array.
+     *
+     * @param kind            The class of the array elements.
+     * @param array           The array to append to.
+     * @param element         The array element to append.
+     * @param allowDuplicates Whether to allow duplicated elements in array.
+     * @return The array containing the appended element.
      */
     @SuppressWarnings("unchecked")
-    public static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
+    @NonNull
+    public static <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element,
             boolean allowDuplicates) {
         final T[] result;
         final int end;
@@ -59,13 +72,14 @@
     /**
      * Combine multiple arrays into a single array.
      *
-     * @param kind The class of the array elements
+     * @param kind   The class of the array elements
      * @param arrays The arrays to combine
-     * @param <T> The class of the array elements (inferred from kind).
+     * @param <T>    The class of the array elements (inferred from kind).
      * @return A single array containing all the elements of the parameter arrays.
      */
     @SuppressWarnings("unchecked")
-    public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
+    @NonNull
+    public static <T> T[] concatElements(Class<T> kind, @Nullable T[]... arrays) {
         if (arrays == null || arrays.length == 0) {
             return createEmptyArray(kind);
         }
diff --git a/telephony/common/com/google/android/mms/pdu/PduPersister.java b/telephony/common/com/google/android/mms/pdu/PduPersister.java
index b237705..8efca0e 100755
--- a/telephony/common/com/google/android/mms/pdu/PduPersister.java
+++ b/telephony/common/com/google/android/mms/pdu/PduPersister.java
@@ -34,6 +34,7 @@
 import android.provider.Telephony.MmsSms.PendingMessages;
 import android.provider.Telephony.Threads;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -1448,9 +1449,9 @@
         final Set<String> myPhoneNumbers = new HashSet<String>();
         if (excludeMyNumber) {
             // Build a list of my phone numbers from the various sims.
-            for (int subid : subscriptionManager.getActiveSubscriptionIdList()) {
+            for (SubscriptionInfo subInfo : subscriptionManager.getActiveSubscriptionInfoList()) {
                 final String myNumber = mContext.getSystemService(TelephonyManager.class).
-                        createForSubscriptionId(subid).getLine1Number();
+                        createForSubscriptionId(subInfo.getSubscriptionId()).getLine1Number();
                 if (myNumber != null) {
                     myPhoneNumbers.add(myNumber);
                 }
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 0659665..9e6dfef 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -98,7 +98,13 @@
             TelephonyManager.NETWORK_TYPE_GSM,
             TelephonyManager.NETWORK_TYPE_TD_SCDMA,
             TelephonyManager.NETWORK_TYPE_IWLAN,
-            TelephonyManager.NETWORK_TYPE_LTE_CA,
+
+            //TODO: In order for @SystemApi methods to use this class, there cannot be any
+            // public hidden members.  This network type is marked as hidden because it is not a
+            // true network type and we are looking to remove it completely from the available list
+            // of network types.
+            //TelephonyManager.NETWORK_TYPE_LTE_CA,
+
             TelephonyManager.NETWORK_TYPE_NR,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -590,4 +596,17 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ImsAudioCodec {
     }
+
+    /**
+     * UICC SIM Application Types
+     */
+    @IntDef(prefix = { "APPTYPE_" }, value = {
+            TelephonyManager.APPTYPE_SIM,
+            TelephonyManager.APPTYPE_USIM,
+            TelephonyManager.APPTYPE_RUIM,
+            TelephonyManager.APPTYPE_CSIM,
+            TelephonyManager.APPTYPE_ISIM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UiccAppType{}
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index eea08dc..fdf8849 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2481,7 +2481,6 @@
      */
     public static final String IMSI_KEY_AVAILABILITY_INT = "imsi_key_availability_int";
 
-
     /**
      * Key identifying if the CDMA Caller ID presentation and suppression MMI codes
      * should be converted to 3GPP CLIR codes when a multimode (CDMA+UMTS+LTE) device is roaming
@@ -3120,6 +3119,16 @@
             "data_switch_validation_timeout_long";
 
     /**
+     * Specifies whether the system should prefix the EAP method to the anonymous identity.
+     * The following prefix will be added if this key is set to TRUE:
+     *   EAP-AKA: "0"
+     *   EAP-SIM: "1"
+     *   EAP-AKA_PRIME: "6"
+     * @hide
+     */
+    public static final String ENABLE_EAP_METHOD_PREFIX_BOOL = "enable_eap_method_prefix_bool";
+
+    /**
      * GPS configs. See the GNSS HAL documentation for more details.
      */
     public static final class Gps {
@@ -3934,6 +3943,7 @@
                 CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR);
         // Default wifi configurations.
         sDefaults.putAll(Wifi.getDefaults());
+        sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index e523fba..ce32dce 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -185,6 +185,14 @@
     @SystemApi
     public abstract @NonNull CellLocation asCellLocation();
 
+    /**
+     * Create and a return a new instance of CellIdentity with location-identifying information
+     * removed.
+     *
+     * @hide
+     */
+    public abstract @NonNull CellIdentity sanitizeLocationInfo();
+
     @Override
     public boolean equals(Object other) {
         if (!(other instanceof CellIdentity)) {
@@ -312,4 +320,23 @@
         return true;
     }
 
+    /** @hide */
+    public static CellIdentity create(android.hardware.radio.V1_5.CellIdentity ci) {
+        if (ci == null) return null;
+        switch (ci.getDiscriminator()) {
+            case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.gsm:
+                return new CellIdentityGsm(ci.gsm());
+            case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.cdma:
+                return new CellIdentityCdma(ci.cdma());
+            case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.lte:
+                return new CellIdentityLte(ci.lte());
+            case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.wcdma:
+                return new CellIdentityWcdma(ci.wcdma());
+            case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.tdscdma:
+                return new CellIdentityTdscdma(ci.tdscdma());
+            case android.hardware.radio.V1_5.CellIdentity.hidl_discriminator.nr:
+                return new CellIdentityNr(ci.nr());
+            default: return null;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 54236b42..1a6bf33 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -128,7 +128,8 @@
     }
 
     /** @hide */
-    public CellIdentityCdma sanitizeLocationInfo() {
+    @Override
+    public @NonNull CellIdentityCdma sanitizeLocationInfo() {
         return new CellIdentityCdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 mAlphaLong, mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 4e4454d..2ecdfce 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -104,7 +104,8 @@
     }
 
     /** @hide */
-    public CellIdentityGsm sanitizeLocationInfo() {
+    @Override
+    public @NonNull CellIdentityGsm sanitizeLocationInfo() {
         return new CellIdentityGsm(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 CellInfo.UNAVAILABLE, mMccStr, mMncStr, mAlphaLong, mAlphaShort);
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index c3fc73b..15c9175 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -121,7 +121,8 @@
     }
 
     /** @hide */
-    public CellIdentityLte sanitizeLocationInfo() {
+    @Override
+    public @NonNull CellIdentityLte sanitizeLocationInfo() {
         return new CellIdentityLte(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 mMccStr, mMncStr, mAlphaLong, mAlphaShort);
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index e3fec7b..f08a580 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -69,7 +69,8 @@
     }
 
     /** @hide */
-    public CellIdentityNr sanitizeLocationInfo() {
+    @Override
+    public @NonNull CellIdentityNr sanitizeLocationInfo() {
         return new CellIdentityNr(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 mMccStr, mMncStr, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort);
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 8f812b6..4bb3a95 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -97,7 +97,8 @@
     }
 
     /** @hide */
-    public CellIdentityTdscdma sanitizeLocationInfo() {
+    @Override
+    public @NonNull CellIdentityTdscdma sanitizeLocationInfo() {
         return new CellIdentityTdscdma(mMccStr, mMncStr, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mAlphaLong, mAlphaShort);
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 556bc32..4ecd134 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
@@ -98,7 +98,8 @@
     }
 
     /** @hide */
-    public CellIdentityWcdma sanitizeLocationInfo() {
+    @Override
+    public @NonNull CellIdentityWcdma sanitizeLocationInfo() {
         return new CellIdentityWcdma(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
                 CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, mMccStr, mMncStr,
                 mAlphaLong, mAlphaShort);
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index ae45307..475c99b 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -18,7 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.hardware.radio.V1_4.CellInfo.Info;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/CellInfoCdma.java b/telephony/java/android/telephony/CellInfoCdma.java
index 30b131f..2b1387c 100644
--- a/telephony/java/android/telephony/CellInfoCdma.java
+++ b/telephony/java/android/telephony/CellInfoCdma.java
@@ -17,11 +17,10 @@
 package android.telephony;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
 
 /**
  * A {@link CellInfo} representing a CDMA cell that provides identity and measurement info.
diff --git a/telephony/java/android/telephony/CellInfoGsm.java b/telephony/java/android/telephony/CellInfoGsm.java
index 137f97e..4f7c7a9 100644
--- a/telephony/java/android/telephony/CellInfoGsm.java
+++ b/telephony/java/android/telephony/CellInfoGsm.java
@@ -17,10 +17,9 @@
 package android.telephony;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
 
 /**
  * A {@link CellInfo} representing a GSM cell that provides identity and measurement info.
diff --git a/telephony/java/android/telephony/CellInfoLte.java b/telephony/java/android/telephony/CellInfoLte.java
index da7b7ab..6d19261 100644
--- a/telephony/java/android/telephony/CellInfoLte.java
+++ b/telephony/java/android/telephony/CellInfoLte.java
@@ -17,7 +17,7 @@
 package android.telephony;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 0133153..64776e3 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -16,13 +16,13 @@
 
 package android.telephony;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-
-import android.annotation.UnsupportedAppUsage;
 import android.telephony.cdma.CdmaCellLocation;
 import android.telephony.gsm.GsmCellLocation;
+
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index fb16d54..a9f3487 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -17,12 +17,11 @@
 package android.telephony;
 
 import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.telephony.Rlog;
 
 import java.util.Objects;
 
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 8df9d23..a6ba9c2 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -17,7 +17,7 @@
 package android.telephony;
 
 import android.annotation.IntRange;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 4dc54f0..34b1385 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -18,11 +18,10 @@
 
 import android.annotation.IntRange;
 import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index 04ec4b6..be85b30 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Describes the cause of a disconnected call. Those disconnect causes can be converted into a more
@@ -360,6 +360,12 @@
      */
     public static final int OUTGOING_EMERGENCY_CALL_PLACED = 80;
 
+    /**
+     * Indicates that incoming call was rejected by the modem before the call went in ringing
+     */
+    public static final int INCOMING_AUTO_REJECTED = 81;
+
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -536,6 +542,8 @@
             return "WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION";
         case OUTGOING_EMERGENCY_CALL_PLACED:
             return "OUTGOING_EMERGENCY_CALL_PLACED";
+            case INCOMING_AUTO_REJECTED:
+                return "INCOMING_AUTO_REJECTED";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
index 92a674c..1c31368 100644
--- a/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
+++ b/telephony/java/android/telephony/JapanesePhoneNumberFormatter.java
@@ -16,7 +16,7 @@
 
 package android.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.text.Editable;
 
 /*
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 023ed30..5f46799 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -24,7 +24,7 @@
 import static android.telephony.TelephonyManager.NETWORK_TYPE_UMTS;
 import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index fc717e7..cbd5ed6 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -46,13 +46,17 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "DOMAIN_", value = {DOMAIN_CS, DOMAIN_PS})
+    @IntDef(prefix = "DOMAIN_", value = {DOMAIN_UNKNOWN, DOMAIN_CS, DOMAIN_PS, DOMAIN_CS_PS})
     public @interface Domain {}
 
+    /** Unknown / Unspecified domain */
+    public static final int DOMAIN_UNKNOWN = 0;
     /** Circuit switching domain */
-    public static final int DOMAIN_CS = 1;
+    public static final int DOMAIN_CS = android.hardware.radio.V1_5.Domain.CS;
     /** Packet switching domain */
-    public static final int DOMAIN_PS = 2;
+    public static final int DOMAIN_PS = android.hardware.radio.V1_5.Domain.PS;
+    /** Applicable to both CS and PS Domain */
+    public static final int DOMAIN_CS_PS = DOMAIN_CS | DOMAIN_PS;
 
     /**
      * Network registration state
@@ -504,11 +508,21 @@
         }
     }
 
+    /** @hide */
+    static @NonNull String domainToString(@Domain int domain) {
+        switch (domain) {
+            case DOMAIN_CS: return "CS";
+            case DOMAIN_PS: return "PS";
+            case DOMAIN_CS_PS: return "CS_PS";
+            default: return "UNKNOWN";
+        }
+    }
+
     @NonNull
     @Override
     public String toString() {
         return new StringBuilder("NetworkRegistrationInfo{")
-                .append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
+                .append(" domain=").append(domainToString(mDomain))
                 .append(" transportType=").append(
                         AccessNetworkConstants.transportTypeToString(mTransportType))
                 .append(" registrationState=").append(registrationStateToString(mRegistrationState))
@@ -646,7 +660,7 @@
      *     .build();
      * </code></pre>
      */
-    public static final class Builder{
+    public static final class Builder {
         @Domain
         private int mDomain;
 
diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
index ac6bcaa..d4ed860 100644
--- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
+++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java
@@ -16,15 +16,14 @@
 
 package android.telephony;
 
-import com.android.i18n.phonenumbers.AsYouTypeFormatter;
-import com.android.i18n.phonenumbers.PhoneNumberUtil;
-
-import android.annotation.UnsupportedAppUsage;
-import android.telephony.PhoneNumberUtils;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.text.Editable;
 import android.text.Selection;
 import android.text.TextWatcher;
 
+import com.android.i18n.phonenumbers.AsYouTypeFormatter;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+
 import java.util.Locale;
 
 /**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 67afa7d..6e86a42 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index bfa6326..98eeacf 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -19,14 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.Annotation.DisconnectCauses;
 import android.telephony.Annotation.PreciseCallStates;
 import android.telephony.Annotation.PreciseDisconnectCauses;
-import android.telephony.DisconnectCause;
-import android.telephony.PreciseDisconnectCause;
 
 import java.util.Objects;
 
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 0610796..31434c1 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.LinkProperties;
 import android.os.Build;
 import android.os.Parcel;
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index 28f6515..bc84738 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -16,9 +16,7 @@
 
 package android.telephony;
 
-import android.annotation.UnsupportedAppUsage;
-import android.hardware.radio.V1_0.RadioTechnology;
-import android.hardware.radio.V1_4.CellInfo.Info;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index ab51d8a..5b12aed 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.Build;
 import android.os.Bundle;
@@ -379,15 +379,15 @@
     /**
      * Create a new ServiceState from a intent notifier Bundle
      *
-     * This method is used by PhoneStateIntentReceiver, CellBroadcastReceiver, and maybe by
-     * external applications.
+     * This method is used to get ServiceState object from extras upon receiving
+     * {@link Intent#ACTION_SERVICE_STATE}.
      *
      * @param m Bundle from intent notifier
      * @return newly created ServiceState
      * @hide
      */
+    @SystemApi
     @NonNull
-    @UnsupportedAppUsage
     public static ServiceState newFromBundle(@NonNull Bundle m) {
         ServiceState ret;
         ret = new ServiceState();
@@ -545,7 +545,7 @@
      * @see #STATE_EMERGENCY_ONLY
      * @see #STATE_POWER_OFF
      *
-     * @return current data registration state {@link RegState}
+     * @return current data registration state
      *
      * @hide
      */
@@ -562,7 +562,7 @@
      * @see #STATE_EMERGENCY_ONLY
      * @see #STATE_POWER_OFF
      *
-     * @return current data registration state {@link RegState}
+     * @return current data registration state
      *
      * @hide
      */
@@ -1281,11 +1281,15 @@
     /**
      * Set intent notifier Bundle based on service state.
      *
+     * Put ServiceState object and its fields into bundle which is used by TelephonyRegistry
+     * to broadcast {@link Intent#ACTION_SERVICE_STATE}.
+     *
      * @param m intent notifier Bundle
      * @hide
+     *
      */
-    @UnsupportedAppUsage
-    public void fillInNotifierBundle(Bundle m) {
+    @SystemApi
+    public void fillInNotifierBundle(@NonNull Bundle m) {
         m.putParcelable(Intent.EXTRA_SERVICE_STATE, this);
         // serviceState already consists of below entries.
         // for backward compatibility, we continue fill in below entries.
@@ -1882,7 +1886,7 @@
 
         synchronized (mNetworkRegistrationInfos) {
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
-                if (networkRegistrationInfo.getDomain() == domain) {
+                if ((networkRegistrationInfo.getDomain() & domain) != 0) {
                     list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
                 }
             }
@@ -1898,7 +1902,6 @@
      * @param transportType The transport type
      * @return The matching {@link NetworkRegistrationInfo}
      * @hide
-     *
      */
     @Nullable
     @SystemApi
@@ -1907,7 +1910,7 @@
         synchronized (mNetworkRegistrationInfos) {
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
                 if (networkRegistrationInfo.getTransportType() == transportType
-                        && networkRegistrationInfo.getDomain() == domain) {
+                        && (networkRegistrationInfo.getDomain() & domain) != 0) {
                     return new NetworkRegistrationInfo(networkRegistrationInfo);
                 }
             }
@@ -1950,9 +1953,18 @@
      * Returns a copy of self with location-identifying information removed.
      * Always clears the NetworkRegistrationInfo's CellIdentity fields, but if removeCoarseLocation
      * is true, clears other info as well.
+     *
+     * @param removeCoarseLocation Whether to also remove coarse location information.
+     *                             if false, it only clears fine location information such as
+     *                             NetworkRegistrationInfo's CellIdentity fields; If true, it will
+     *                             also remove other location information such as operator's MCC
+     *                             and MNC.
+     * @return the copied ServiceState with location info sanitized.
      * @hide
      */
-    public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
+    @SystemApi
+    @NonNull
+    public ServiceState createLocationInfoSanitizedCopy(boolean removeCoarseLocation) {
         ServiceState state = new ServiceState(this);
         synchronized (state.mNetworkRegistrationInfos) {
             List<NetworkRegistrationInfo> networkRegistrationInfos =
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 9aafc1b..3350a33 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -17,7 +17,8 @@
 package android.telephony;
 
 import android.annotation.NonNull;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -275,8 +276,8 @@
      *
      * @hide
      */
-    @UnsupportedAppUsage
-    public SignalStrength(SignalStrength s) {
+    @SystemApi
+    public SignalStrength(@NonNull SignalStrength s) {
         copyFrom(s);
     }
 
@@ -332,17 +333,16 @@
     /**
      * {@link Parcelable.Creator}
      *
-     * @hide
      */
-    @UnsupportedAppUsage
-    public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR = new Parcelable.Creator() {
-        public SignalStrength createFromParcel(Parcel in) {
-            return new SignalStrength(in);
-        }
+    public static final @android.annotation.NonNull Parcelable.Creator<SignalStrength> CREATOR =
+            new Parcelable.Creator<SignalStrength>() {
+                public SignalStrength createFromParcel(Parcel in) {
+                    return new SignalStrength(in);
+                }
 
-        public SignalStrength[] newArray(int size) {
-            return new SignalStrength[size];
-        }
+                public SignalStrength[] newArray(int size) {
+                    return new SignalStrength[size];
+                }
     };
 
     /**
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 045d1eb..3c67094 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -557,7 +557,7 @@
     public ContentValues getContentValues() {
         ContentValues cv = new ContentValues(16);
         cv.put(CellBroadcasts.SLOT_INDEX, mSlotIndex);
-        cv.put(CellBroadcasts.SUB_ID, mSubId);
+        cv.put(CellBroadcasts.SUBSCRIPTION_ID, mSubId);
         cv.put(CellBroadcasts.GEOGRAPHICAL_SCOPE, mGeographicalScope);
         if (mLocation.getPlmn() != null) {
             cv.put(CellBroadcasts.PLMN, mLocation.getPlmn());
@@ -621,7 +621,7 @@
         int format = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_FORMAT));
         int priority = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.MESSAGE_PRIORITY));
         int slotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SLOT_INDEX));
-        int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUB_ID));
+        int subId = cursor.getInt(cursor.getColumnIndexOrThrow(CellBroadcasts.SUBSCRIPTION_ID));
 
         String plmn;
         int plmnColumn = cursor.getColumnIndex(CellBroadcasts.PLMN);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 7215ef8..fbe355e 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -25,9 +25,9 @@
 import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.database.CursorWindow;
@@ -2504,22 +2504,31 @@
     public static final String MESSAGE_STATUS_READ = "read";
 
     /**
-     * Get carrier-dependent configuration values.
+     * Get carrier-dependent MMS configuration values.
      *
      * <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
-     * applications or the Telephony framework and will never trigger an SMS disambiguation
-     * dialog. If this method is called on a device that has multiple active subscriptions, this
-     * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined
-     * default subscription is defined, the subscription ID associated with this message will be
-     * INVALID, which will result in the operation being completed on the subscription associated
-     * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the
-     * operation is performed on the correct subscription.
+     * applications or the Telephony framework and will never trigger an SMS disambiguation dialog.
+     * If this method is called on a device that has multiple active subscriptions, this {@link
+     * SmsManager} instance has been created with {@link #getDefault()}, and no user-defined default
+     * subscription is defined, the subscription ID associated with this message will be INVALID,
+     * which will result in the operation being completed on the subscription associated with
+     * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the operation is
+     * performed on the correct subscription.
      * </p>
      *
-     * @return bundle key/values pairs of configuration values
+     * @return the bundle key/values pairs that contains MMS configuration values
      */
+    @Nullable
     public Bundle getCarrierConfigValues() {
-        return MmsManager.getInstance().getCarrierConfigValues(getSubscriptionId());
+        try {
+            ISms iSms = getISmsService();
+            if (iSms != null) {
+                return iSms.getCarrierConfigValuesForSubscriber(getSubscriptionId());
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return null;
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 2897358..c0bc29d 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -20,7 +20,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.StringDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.os.Binder;
 import android.text.TextUtils;
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 94085e9..2c0a1c9 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -18,7 +18,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 7380b31..22eed6e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -33,9 +33,9 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.BroadcastOptions;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
@@ -1199,12 +1199,17 @@
     }
 
     /**
-     * Get the active SubscriptionInfo associated with the iccId
+     * Gets an active SubscriptionInfo {@link SubscriptionInfo} associated with the Sim IccId.
+     *
      * @param iccId the IccId of SIM card
      * @return SubscriptionInfo, maybe null if its not active
+     *
      * @hide
      */
-    public SubscriptionInfo getActiveSubscriptionInfoForIccIndex(String iccId) {
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @Nullable
+    @SystemApi
+    public SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String iccId) {
         if (VDBG) logd("[getActiveSubscriptionInfoForIccIndex]+ iccId=" + iccId);
         if (iccId == null) {
             logd("[getActiveSubscriptionInfoForIccIndex]- null iccid");
@@ -1323,6 +1328,11 @@
      * The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex}
      * then by {@link SubscriptionInfo#getSubscriptionId}.
      *
+     * Hidden subscriptions refer to those are not meant visible to the users.
+     * For example, an opportunistic subscription that is grouped with other
+     * subscriptions should remain invisible to users as they are only functionally
+     * supplementary to primary ones.
+     *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see
      * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
@@ -2176,20 +2186,35 @@
     }
 
     /**
-     * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+     * Get visible subscription Id(s) of the currently active SIM(s).
      *
      * @return the list of subId's that are active,
-     *         is never null but the length maybe 0.
+     *         is never null but the length may be 0.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public @NonNull int[] getActiveSubscriptionIdList() {
         return getActiveSubscriptionIdList(/* visibleOnly */ true);
     }
 
     /**
-     * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+     * Get both hidden and visible subscription Id(s) of the currently active SIM(s).
      *
+     * Hidden subscriptions refer to those are not meant visible to the users.
+     * For example, an opportunistic subscription that is grouped with other
+     * subscriptions should remain invisible to users as they are only functionally
+     * supplementary to primary ones.
+     *
+     * @return the list of subId's that are active,
+     *         is never null but the length may be 0.
+     * @hide
+     */
+    @SystemApi
+    public @NonNull int[] getActiveAndHiddenSubscriptionIdList() {
+        return getActiveSubscriptionIdList(/* visibleOnly */false);
+    }
+
+    /**
      * @return a non-null list of subId's that are active.
      *
      * @hide
@@ -3282,31 +3307,6 @@
         return subId;
     }
 
-    /**
-     * Set whether a subscription always allows MMS connection. If true, MMS network
-     * request will be accepted by telephony even if user turns "mobile data" off
-     * on this subscription.
-     *
-     * @param subId which subscription it's setting to.
-     * @param alwaysAllow whether Mms data is always allowed.
-     * @return whether operation is successful.
-     *
-     * @hide
-     */
-    public boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow) {
-        try {
-            ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
-            if (iSub != null) {
-                return iSub.setAlwaysAllowMmsData(subId, alwaysAllow);
-            }
-        } catch (RemoteException ex) {
-            if (!isSystemProcess()) {
-                ex.rethrowAsRuntimeException();
-            }
-        }
-        return false;
-    }
-
     private interface CallISubMethodHelper {
         int callMethod(ISub iSub) throws RemoteException;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 365ab22..df8c26d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17,6 +17,8 @@
 package android.telephony;
 
 import static android.content.Context.TELECOM_SERVICE;
+import static android.provider.Telephony.Carriers.DPC_URI;
+import static android.provider.Telephony.Carriers.INVALID_APN_ID;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
@@ -34,16 +36,17 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
 import android.annotation.WorkerThread;
 import android.app.ActivityThread;
 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.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
 import android.net.Uri;
@@ -73,7 +76,9 @@
 import android.telephony.Annotation.NetworkType;
 import android.telephony.Annotation.RadioPowerState;
 import android.telephony.Annotation.SimActivationState;
+import android.telephony.Annotation.UiccAppType;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
+import android.telephony.data.ApnSetting;
 import android.telephony.data.ApnSetting.MvnoType;
 import android.telephony.emergency.EmergencyNumber;
 import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
@@ -1538,6 +1543,48 @@
     public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
             = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
 
+    /**
+     * Broadcast Action: The default data subscription has changed in a multi-SIM device.
+     * This has the following extra values:</p>
+     * <ul>
+     *   <li><em>subscription</em> - A int, the current data default subscription.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
+            = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+
+    /**
+     * Broadcast Action: The default voice subscription has changed in a mult-SIm device.
+     * This has the following extra values:</p>
+     * <ul>
+     *   <li><em>subscription</em> - A int, the current voice default subscription.</li>
+     * </ul>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED
+            = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+
+    /**
+     * Broadcast Action: This triggers a client initiated OMA-DM session to the OMA server.
+     * <p class="note">
+     * Open Mobile Alliance (OMA) Device Management (DM).
+     *
+     * This intent is used by the system components to trigger OMA-DM
+     *
+     * @hide
+     */
+    @SystemApi
+    @SuppressLint("ActionValue")
+    public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE
+            = "com.android.omadm.service.CONFIGURATION_UPDATE";
+
     //
     //
     // Device Info
@@ -6781,19 +6828,6 @@
         }
     }
 
-    /**
-     * UICC SIM Application Types
-     * @hide
-     */
-    @IntDef(prefix = { "APPTYPE_" }, value = {
-            APPTYPE_SIM,
-            APPTYPE_USIM,
-            APPTYPE_RUIM,
-            APPTYPE_CSIM,
-            APPTYPE_ISIM
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface UiccAppType{}
     /** UICC application type is SIM */
     public static final int APPTYPE_SIM = PhoneConstants.APPTYPE_SIM;
     /** UICC application type is USIM */
@@ -7739,12 +7773,12 @@
     /**
      * Check whether DUN APN is required for tethering.
      * <p>
-     * Requires Permission: READ_PRIVILEGED_PHONE_STATE.
+     * Requires Permission: MODIFY_PHONE_STATE.
      *
      * @return {@code true} if DUN APN is required for tethering.
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     @SystemApi
     public boolean isTetheringApnRequired() {
         return isTetheringApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId()));
@@ -8213,9 +8247,9 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.supplyPin(pin);
+                return telephony.supplyPinForSubscriber(getSubId(), pin);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#supplyPin", e);
+            Log.e(TAG, "Error calling ITelephony#supplyPinForSubscriber", e);
         }
         return false;
     }
@@ -8227,9 +8261,9 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.supplyPuk(puk, pin);
+                return telephony.supplyPukForSubscriber(getSubId(), puk, pin);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#supplyPuk", e);
+            Log.e(TAG, "Error calling ITelephony#supplyPukForSubscriber", e);
         }
         return false;
     }
@@ -8241,9 +8275,9 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.supplyPinReportResult(pin);
+                return telephony.supplyPinReportResultForSubscriber(getSubId(), pin);
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#supplyPinReportResult", e);
+            Log.e(TAG, "Error calling ITelephony#supplyPinReportResultForSubscriber", e);
         }
         return new int[0];
     }
@@ -8255,7 +8289,7 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.supplyPukReportResult(puk, pin);
+                return telephony.supplyPukReportResultForSubscriber(getSubId(), puk, pin);
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#]", e);
         }
@@ -10486,14 +10520,15 @@
     /**
      * Policy control of data connection. Usually used when data limit is passed.
      * @param enabled True if enabling the data, otherwise disabling.
-     * @param subId sub id
      * @hide
      */
-    public void setPolicyDataEnabled(boolean enabled, int subId) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setPolicyDataEnabled(boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.setPolicyDataEnabled(enabled, subId);
+                service.setPolicyDataEnabled(enabled, getSubId());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setPolicyDataEnabled", e);
@@ -10566,7 +10601,8 @@
      *
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public boolean isManualNetworkSelectionAllowed() {
         try {
             ITelephony telephony = getITelephony();
@@ -11872,6 +11908,88 @@
     }
 
     /**
+     * Returns a list of APNs set as overrides by the device policy manager via
+     * {@link #addDevicePolicyOverrideApn}.
+     * This method must only be called from the system or phone processes.
+     *
+     * @param context Context to use.
+     * @return {@link List} of APNs that have been set as overrides.
+     * @throws {@link SecurityException} if the caller is not the system or phone process.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    // TODO: add new permission tag indicating that this is system-only.
+    public @NonNull List<ApnSetting> getDevicePolicyOverrideApns(@NonNull Context context) {
+        try (Cursor cursor = context.getContentResolver().query(DPC_URI, null, null, null, null)) {
+            if (cursor == null) {
+                return Collections.emptyList();
+            }
+            List<ApnSetting> apnList = new ArrayList<ApnSetting>();
+            cursor.moveToPosition(-1);
+            while (cursor.moveToNext()) {
+                ApnSetting apn = ApnSetting.makeApnSetting(cursor);
+                apnList.add(apn);
+            }
+            return apnList;
+        }
+    }
+
+    /**
+     * Used by the device policy manager to add a new override APN.
+     * This method must only be called from the system or phone processes.
+     *
+     * @param context Context to use.
+     * @param apnSetting The {@link ApnSetting} describing the new APN.
+     * @return An integer, corresponding to a primary key in a database, that allows the caller to
+     *         modify the APN in the future via {@link #modifyDevicePolicyOverrideApn}, or
+     *         {@link android.provider.Telephony.Carriers.INVALID_APN_ID} if the override operation
+     *         failed.
+     * @throws {@link SecurityException} if the caller is not the system or phone process.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    // TODO: add new permission tag indicating that this is system-only.
+    public int addDevicePolicyOverrideApn(@NonNull Context context,
+            @NonNull ApnSetting apnSetting) {
+        Uri resultUri = context.getContentResolver().insert(DPC_URI, apnSetting.toContentValues());
+
+        int resultId = INVALID_APN_ID;
+        if (resultUri != null) {
+            try {
+                resultId = Integer.parseInt(resultUri.getLastPathSegment());
+            } catch (NumberFormatException e) {
+                Rlog.e(TAG, "Failed to parse inserted override APN id: "
+                        + resultUri.getLastPathSegment());
+            }
+        }
+        return resultId;
+    }
+
+    /**
+     * Used by the device policy manager to modify an override APN.
+     * This method must only be called from the system or phone processes.
+     *
+     * @param context Context to use.
+     * @param apnId The integer key of the APN to modify, as returned by
+     *              {@link #addDevicePolicyOverrideApn}
+     * @param apnSetting The {@link ApnSetting} describing the updated APN.
+     * @return {@code true} if successful, {@code false} otherwise.
+     * @throws {@link SecurityException} if the caller is not the system or phone process.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    // TODO: add new permission tag indicating that this is system-only.
+    public boolean modifyDevicePolicyOverrideApn(@NonNull Context context, int apnId,
+            @NonNull ApnSetting apnSetting) {
+        return context.getContentResolver().update(
+                Uri.withAppendedPath(DPC_URI, Integer.toString(apnId)),
+                apnSetting.toContentValues(), null, null) > 0;
+    }
+
+    /**
      * Return whether data is enabled for certain APN type. This will tell if framework will accept
      * corresponding network requests on a subId.
      *
@@ -11884,7 +12002,7 @@
      *  1) User data is turned on, or
      *  2) APN is un-metered for this subscription, or
      *  3) APN type is whitelisted. E.g. MMS is whitelisted if
-     *  {@link SubscriptionManager#setAlwaysAllowMmsData} is turned on.
+     *  {@link #setAlwaysAllowMmsData(boolean)} is turned on.
      *
      * @param apnType Value indicating the apn type. Apn types are defined in {@link ApnSetting}.
      * @return whether data is enabled for a apn type.
@@ -12008,4 +12126,30 @@
         }
         return false;
     }
+
+    /**
+     * Set whether the specific sim card always allows MMS connection. If true, MMS network
+     * request will be accepted by telephony even if user turns "mobile data" off
+     * on this specific sim card.
+     *
+     * @param alwaysAllow whether Mms data is always allowed.
+     * @return whether operation is successful.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public boolean setAlwaysAllowMmsData(boolean alwaysAllow) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                return service.setAlwaysAllowMmsData(getSubId(), alwaysAllow);
+            }
+        } catch (RemoteException ex) {
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
+        }
+        return false;
+    }
 }
diff --git a/telephony/java/android/telephony/VoLteServiceState.java b/telephony/java/android/telephony/VoLteServiceState.java
index f65c7b0..414b999 100644
--- a/telephony/java/android/telephony/VoLteServiceState.java
+++ b/telephony/java/android/telephony/VoLteServiceState.java
@@ -16,12 +16,11 @@
 
 package android.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.telephony.Rlog;
 
 /**
  * Contains LTE network state related information.
diff --git a/telephony/java/android/telephony/cdma/CdmaCellLocation.java b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
index 45b1e47..9bc39a0 100644
--- a/telephony/java/android/telephony/cdma/CdmaCellLocation.java
+++ b/telephony/java/android/telephony/cdma/CdmaCellLocation.java
@@ -16,7 +16,7 @@
 
 package android.telephony.cdma;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.telephony.CellLocation;
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 034fc22..dbfb6a2 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1056,6 +1056,11 @@
     }
 
     /** @hide */
+    public boolean isEmergencyApn() {
+        return hasApnType(TYPE_EMERGENCY);
+    }
+
+    /** @hide */
     public boolean canHandleType(@ApnType int type) {
         if (!mCarrierEnabled) {
             return false;
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index cb27f64..23d46ba 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -17,17 +17,18 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.UiccAccessRule;
+
+import com.android.internal.util.Preconditions;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
-import com.android.internal.util.Preconditions;
-
 /**
  * Information about a subscription which is downloadable to an eUICC using
  * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
diff --git a/telephony/java/android/telephony/euicc/EuiccInfo.java b/telephony/java/android/telephony/euicc/EuiccInfo.java
index 91ebb6c..467d268 100644
--- a/telephony/java/android/telephony/euicc/EuiccInfo.java
+++ b/telephony/java/android/telephony/euicc/EuiccInfo.java
@@ -16,7 +16,7 @@
 package android.telephony.euicc;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/telephony/java/android/telephony/gsm/GsmCellLocation.java b/telephony/java/android/telephony/gsm/GsmCellLocation.java
index d6780ce..30cea0e 100644
--- a/telephony/java/android/telephony/gsm/GsmCellLocation.java
+++ b/telephony/java/android/telephony/gsm/GsmCellLocation.java
@@ -16,7 +16,7 @@
 
 package android.telephony.gsm;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Bundle;
 import android.telephony.CellLocation;
diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
index 9f09d7a..d53a2e6 100644
--- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
+++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 87e5391..bc60d81 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -279,6 +279,14 @@
                                   "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
 
     /**
+     * CallDisconnectCause: Specify call disconnect cause. This extra should be a code
+     * corresponding to ImsReasonInfo and should only be populated in the case that the
+     * call has already been missed
+     */
+    public static final String EXTRA_CALL_DISCONNECT_CAUSE =
+                                 "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE";
+
+    /**
      * Extra key which the RIL can use to indicate the radio technology used for a call.
      * Valid values are:
      * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE},
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index f4b2cef..0d6b31d 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java
index 77bd984..9cce95f 100644
--- a/telephony/java/android/telephony/ims/ImsSsInfo.java
+++ b/telephony/java/android/telephony/ims/ImsSsInfo.java
@@ -21,7 +21,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
index b7ab0a0..b70fd64 100644
--- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
+++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
index 270e693..569c6d5 100644
--- a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
+++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java
@@ -18,7 +18,7 @@
 
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Looper;
diff --git a/telephony/java/android/telephony/ims/compat/ImsService.java b/telephony/java/android/telephony/ims/compat/ImsService.java
index 97a8517..eafbb14 100644
--- a/telephony/java/android/telephony/ims/compat/ImsService.java
+++ b/telephony/java/android/telephony/ims/compat/ImsService.java
@@ -17,8 +17,8 @@
 package android.telephony.ims.compat;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
index de4f174..5a9e8e2 100644
--- a/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/ImsFeature.java
@@ -17,7 +17,7 @@
 package android.telephony.ims.compat.feature;
 
 import android.annotation.IntDef;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.os.IInterface;
 import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
index 3fd356a..b52c371 100644
--- a/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/compat/feature/MMTelFeature.java
@@ -16,8 +16,8 @@
 
 package android.telephony.ims.compat.feature;
 
-import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.ims.ImsCallProfile;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index d77f78e..acab738 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -16,7 +16,7 @@
 
 package android.telephony.ims.compat.stub;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.CallQuality;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
index e55c3d0..aae6f92 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsConfigImplBase.java
@@ -16,7 +16,7 @@
 
 package android.telephony.ims.compat.stub;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
 import android.os.RemoteException;
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
index e2024742..ce291d4 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsUtListenerImplBase.java
@@ -16,7 +16,7 @@
 
 package android.telephony.ims.compat.stub;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.telephony.ims.ImsCallForwardInfo;
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index e80087d..50b63bd 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -16,13 +16,12 @@
 
 package com.android.ims;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
 import android.os.Message;
 import android.telephony.ims.ImsCallForwardInfo;
 import android.telephony.ims.ImsSsInfo;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 /**
  * Provides APIs for the supplementary service settings using IMS (Ut interface).
  * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 9e786ce..1449a62 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -15,9 +15,9 @@
  */
 package com.android.internal.telephony;
 
-import com.android.internal.util.Protocol;
+import android.compat.annotation.UnsupportedAppUsage;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import com.android.internal.util.Protocol;
 
 /**
  * @hide
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 9f4d066..c07a171 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -1,18 +1,18 @@
 /*
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
+ * Copyright 2007, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 
 package com.android.internal.telephony;
 
@@ -22,7 +22,7 @@
 import com.android.internal.telephony.SmsRawData;
 
 /**
- * Interface for applications to access the ICC phone book.
+ * Service interface to handle SMS API requests
  *
  * See also SmsManager.java.
  */
@@ -542,6 +542,13 @@
                 in List<PendingIntent> deliveryIntents);
 
     /**
+     * Get carrier-dependent configuration values.
+     *
+     * @param subId the subscription Id
+     */
+    Bundle getCarrierConfigValuesForSubscriber(int subId);
+
+    /**
      * Create an app-only incoming SMS request for the calling package.
      *
      * If an incoming text contains the token returned by this method the provided
diff --git a/telephony/java/com/android/internal/telephony/ISmsImplBase.java b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
index 2430d82..ddd3457 100644
--- a/telephony/java/com/android/internal/telephony/ISmsImplBase.java
+++ b/telephony/java/com/android/internal/telephony/ISmsImplBase.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.net.Uri;
+import android.os.Bundle;
 
 import java.util.List;
 
@@ -185,6 +186,11 @@
     }
 
     @Override
+    public Bundle getCarrierConfigValuesForSubscriber(int subId) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
         throw new UnsupportedOperationException();
     }
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index cc02a40..571efce 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -295,8 +295,6 @@
 
     boolean isActiveSubId(int subId, String callingPackage, String callingFeatureId);
 
-    boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow);
-
     int getActiveDataSubscriptionId();
 
     boolean canDisablePhysicalSubscription();
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 57fda9b1..b846a10 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -117,13 +117,6 @@
      */
     boolean isRadioOnForSubscriberWithFeature(int subId, String callingPackage, String callingFeatureId);
 
-    /**
-     * Supply a pin to unlock the SIM.  Blocks until a result is determined.
-     * @param pin The pin to check.
-     * @return whether the operation was a success.
-     */
-    @UnsupportedAppUsage
-    boolean supplyPin(String pin);
 
     /**
      * Supply a pin to unlock the SIM for particular subId.
@@ -139,15 +132,6 @@
      *  Blocks until a result is determined.
      * @param puk The puk to check.
      *        pin The new pin to be set in SIM
-     * @return whether the operation was a success.
-     */
-    boolean supplyPuk(String puk, String pin);
-
-    /**
-     * Supply puk to unlock the SIM and set SIM pin to new pin.
-     *  Blocks until a result is determined.
-     * @param puk The puk to check.
-     *        pin The new pin to be set in SIM
      * @param subId user preferred subId.
      * @return whether the operation was a success.
      */
@@ -160,15 +144,6 @@
      * @return retValue[0] = Phone.PIN_RESULT_SUCCESS on success. Otherwise error code
      *         retValue[1] = number of attempts remaining if known otherwise -1
      */
-    int[] supplyPinReportResult(String pin);
-
-    /**
-     * Supply a pin to unlock the SIM.  Blocks until a result is determined.
-     * Returns a specific success/error code.
-     * @param pin The pin to check.
-     * @return retValue[0] = Phone.PIN_RESULT_SUCCESS on success. Otherwise error code
-     *         retValue[1] = number of attempts remaining if known otherwise -1
-     */
     int[] supplyPinReportResultForSubscriber(int subId, String pin);
 
     /**
@@ -180,17 +155,6 @@
      * @return retValue[0] = Phone.PIN_RESULT_SUCCESS on success. Otherwise error code
      *         retValue[1] = number of attempts remaining if known otherwise -1
      */
-    int[] supplyPukReportResult(String puk, String pin);
-
-    /**
-     * Supply puk to unlock the SIM and set SIM pin to new pin.
-     * Blocks until a result is determined.
-     * Returns a specific success/error code
-     * @param puk The puk to check
-     *        pin The pin to check.
-     * @return retValue[0] = Phone.PIN_RESULT_SUCCESS on success. Otherwise error code
-     *         retValue[1] = number of attempts remaining if known otherwise -1
-     */
     int[] supplyPukReportResultForSubscriber(int subId, String puk, String pin);
 
     /**
@@ -2154,6 +2118,11 @@
     boolean isDataAllowedInVoiceCall(int subId);
 
     /**
+     * Set whether a subscription always allows MMS connection.
+     */
+    boolean setAlwaysAllowMmsData(int subId, boolean allow);
+
+    /**
      * Command line command to enable or disable handling of CEP data for test purposes.
      */
     oneway void setCepEnabled(boolean isCepEnabled);
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 25f03c2..94dfae8 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -15,11 +15,10 @@
  */
 package com.android.internal.telephony;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.telephony.TelephonyManager;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 /**
  * {@hide}
  */
diff --git a/telephony/java/com/android/internal/telephony/OperatorInfo.java b/telephony/java/com/android/internal/telephony/OperatorInfo.java
index 59c39b1..64d7863 100644
--- a/telephony/java/com/android/internal/telephony/OperatorInfo.java
+++ b/telephony/java/com/android/internal/telephony/OperatorInfo.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index fadb573..51701eb 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -15,7 +15,7 @@
  */
 package com.android.internal.telephony;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * @hide
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 9cbcd7f..284544b 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -16,10 +16,9 @@
 
 package com.android.internal.telephony;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.sysprop.TelephonyProperties;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.util.Optional;
 
 /**
@@ -555,4 +554,5 @@
     int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
     int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
     int RIL_UNSOL_UICC_APPLICATIONS_ENABLEMENT_CHANGED = 1103;
+    int RIL_UNSOL_REGISTRATION_FAILED = 1104;
 }
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index d672642..1d6ec2d 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.telephony.Rlog;
@@ -25,8 +26,6 @@
 import com.android.internal.telephony.util.TelephonyUtils;
 import com.android.internal.telephony.util.XmlUtils;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 public class Sms7BitEncodingTranslator {
     private static final String TAG = "Sms7BitEncodingTranslator";
     @UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/SmsAddress.java b/telephony/java/com/android/internal/telephony/SmsAddress.java
index 2a8de8c..f18256a 100644
--- a/telephony/java/com/android/internal/telephony/SmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/SmsAddress.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 public abstract class SmsAddress {
     // From TS 23.040 9.1.2.5 and TS 24.008 table 10.5.118
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index dd77b01..ab3fdf4 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import com.android.internal.util.HexDump;
 
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index d6632f3..084882b 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.telephony.SmsMessage;
 import android.text.TextUtils;
diff --git a/telephony/java/com/android/internal/telephony/SmsRawData.java b/telephony/java/com/android/internal/telephony/SmsRawData.java
index 18727f3..9776c8f 100644
--- a/telephony/java/com/android/internal/telephony/SmsRawData.java
+++ b/telephony/java/com/android/internal/telephony/SmsRawData.java
@@ -17,7 +17,7 @@
 
 package com.android.internal.telephony;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 08c536b..f78c65f 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -304,7 +304,7 @@
      * </ul>
      */
     public static final String ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED
-            = "android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED";
+            = TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED;
 
     /**
      * Broadcast Action: The default voice subscription has changed.  This has the following
@@ -314,7 +314,7 @@
      * </ul>
      */
     public static final String ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED
-            = "android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED";
+            = TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED;
 
     /**
      * Broadcast Action: The default sms subscription has changed.  This has the following
@@ -433,7 +433,7 @@
      * Broadcast action to trigger CI OMA-DM Session.
      */
     public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
-            "com.android.omadm.service.CONFIGURATION_UPDATE";
+            TelephonyManager.ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE;
 
     /**
      * Broadcast action to trigger the Carrier Certificate download.
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 4e42c20..ff70f8b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Contains a list of string constants used to get or set telephone properties
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 1f7715b..e75c593 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.cdma;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.sysprop.TelephonyProperties;
 import android.telephony.PhoneNumberUtils;
@@ -41,8 +42,6 @@
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.HexDump;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
diff --git a/telephony/java/com/android/internal/telephony/cdma/UserData.java b/telephony/java/com/android/internal/telephony/cdma/UserData.java
index 7187ae4..524cb0c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/UserData.java
@@ -16,13 +16,12 @@
 
 package com.android.internal.telephony.cdma.sms;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.SparseIntArray;
 
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.util.HexDump;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 public class UserData {
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index d9be548..b5af646 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.cdma.sms;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.telephony.Rlog;
 import android.telephony.SmsCbCmasInfo;
@@ -31,8 +32,6 @@
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.BitwiseOutputStream;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index a82b975..6f0de34 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony.cdma.sms;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index c924ab3..9e2d29c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -16,8 +16,7 @@
 
 package com.android.internal.telephony.cdma.sms;
 
-
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.telephony.cdma.CdmaSmsCbProgramData;
 
 public final class SmsEnvelope {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
index 17f69b3..5409c09 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
@@ -16,13 +16,12 @@
 
 package com.android.internal.telephony.gsm;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.telephony.PhoneNumberUtils;
 
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsAddress;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.text.ParseException;
 
 public class GsmSmsAddress extends SmsAddress {
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 216f616..d190345 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -16,13 +16,12 @@
 
 package com.android.internal.telephony.gsm;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.SmsCbEtwsInfo;
 
 import com.android.internal.telephony.SmsConstants;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.util.Arrays;
 import java.util.Locale;
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index da32c8c..0681dc1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -25,6 +25,7 @@
 import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
 import static com.android.internal.telephony.SmsConstants.MessageClass;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
@@ -38,8 +39,6 @@
 import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.uicc.IccUtils;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.text.ParseException;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index f2d4624..eed9a86 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.uicc;
 
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
 import android.graphics.Bitmap;
@@ -25,8 +26,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet;
 
-import dalvik.annotation.compat.UnsupportedAppUsage;
-
 import java.io.UnsupportedEncodingException;
 import java.util.List;
 
diff --git a/test-base/hiddenapi/Android.bp b/test-base/hiddenapi/Android.bp
index c4e0fab..c202467 100644
--- a/test-base/hiddenapi/Android.bp
+++ b/test-base/hiddenapi/Android.bp
@@ -25,5 +25,8 @@
 
     srcs: ["src/**/*.java"],
 
-    libs: ["android.test.base"],
+    libs: [
+        "android.test.base",
+        "unsupportedappusage",
+    ],
 }
diff --git a/test-base/hiddenapi/src/android/test/AndroidTestCase.java b/test-base/hiddenapi/src/android/test/AndroidTestCase.java
index 2b9beb1..fcb8d43 100644
--- a/test-base/hiddenapi/src/android/test/AndroidTestCase.java
+++ b/test-base/hiddenapi/src/android/test/AndroidTestCase.java
@@ -16,7 +16,7 @@
 
 package android.test;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 
 import junit.framework.TestCase;
diff --git a/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java b/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java
index 139cd18..a48a56c 100644
--- a/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java
+++ b/test-base/hiddenapi/src/android/test/InstrumentationTestCase.java
@@ -16,7 +16,7 @@
 
 package android.test;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import junit.framework.TestCase;
 
diff --git a/test-base/hiddenapi/src/junit/framework/TestCase.java b/test-base/hiddenapi/src/junit/framework/TestCase.java
index 5a54861..59fbe2b 100644
--- a/test-base/hiddenapi/src/junit/framework/TestCase.java
+++ b/test-base/hiddenapi/src/junit/framework/TestCase.java
@@ -16,7 +16,7 @@
 
 package junit.framework;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * Stub only
diff --git a/test-base/hiddenapi/src/junit/framework/TestSuite.java b/test-base/hiddenapi/src/junit/framework/TestSuite.java
index 368c661..32305c9 100644
--- a/test-base/hiddenapi/src/junit/framework/TestSuite.java
+++ b/test-base/hiddenapi/src/junit/framework/TestSuite.java
@@ -16,7 +16,7 @@
 
 package junit.framework;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 import java.lang.reflect.Method;
 
diff --git a/tests/ApkVerityTest/TEST_MAPPING b/tests/ApkVerityTest/TEST_MAPPING
index a660839..72d9614 100644
--- a/tests/ApkVerityTest/TEST_MAPPING
+++ b/tests/ApkVerityTest/TEST_MAPPING
@@ -1,15 +1,12 @@
 {
   "presubmit": [
+    {
+      "name": "ApkVerityTest"
+    },
     // nextgen test only runs during postsubmit.
     {
       "name": "ApkVerityTest",
       "keywords": ["nextgen"]
     }
-  ],
-  "postsubmit": [
-    // TODO: move to presubmit once it's confirmed stable.
-    {
-      "name": "ApkVerityTest"
-    }
   ]
 }
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 2445a6a..20d0e96 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -27,6 +27,7 @@
 
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.util.CommandResult;
@@ -412,6 +413,7 @@
                         break;
                     }
                     try {
+                        CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath));
                         Thread.sleep(1000);
                         String pid = expectRemoteCommandToSucceed("pidof system_server");
                         mDevice.executeShellV2Command("kill -10 " + pid);  // force GC
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index daa85bd..f6699fa 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.UserManager;
@@ -1122,4 +1123,39 @@
             InstallUtils.dropShellPermissionIdentity();
         }
     }
+
+    @Test
+    public void testRollbackDataPolicy() throws Exception {
+        try {
+            InstallUtils.adoptShellPermissionIdentity(
+                    Manifest.permission.INSTALL_PACKAGES,
+                    Manifest.permission.DELETE_PACKAGES,
+                    Manifest.permission.TEST_MANAGE_ROLLBACKS);
+
+            Uninstall.packages(TestApp.A, TestApp.B);
+            Install.multi(TestApp.A1, TestApp.B1).commit();
+            // Write user data version = 1
+            InstallUtils.processUserData(TestApp.A);
+            InstallUtils.processUserData(TestApp.B);
+
+            Install a2 = Install.single(TestApp.A2)
+                    .setEnableRollback(PackageManager.RollbackDataPolicy.WIPE);
+            Install b2 = Install.single(TestApp.B2)
+                    .setEnableRollback(PackageManager.RollbackDataPolicy.RESTORE);
+            Install.multi(a2, b2).setEnableRollback().commit();
+            // Write user data version = 2
+            InstallUtils.processUserData(TestApp.A);
+            InstallUtils.processUserData(TestApp.B);
+
+            RollbackInfo info = RollbackUtils.getAvailableRollback(TestApp.A);
+            RollbackUtils.rollback(info.getRollbackId());
+            // Read user data version from userdata.txt
+            // A's user data version is -1 for user data is wiped.
+            // B's user data version is 1 as rollback committed.
+            assertThat(InstallUtils.getUserDataVersion(TestApp.A)).isEqualTo(-1);
+            assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
+        } finally {
+            InstallUtils.dropShellPermissionIdentity();
+        }
+    }
 }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index bb3906d..40169b8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -384,6 +384,44 @@
         RollbackUtils.getRollbackManager().expireRollbackForPackage(getModuleMetadataPackageName());
     }
 
+    @Test
+    public void testRollbackDataPolicy_Phase1() throws Exception {
+        Uninstall.packages(TestApp.A, TestApp.B);
+        Install.multi(TestApp.A1, TestApp.B1).commit();
+        // Write user data version = 1
+        InstallUtils.processUserData(TestApp.A);
+        InstallUtils.processUserData(TestApp.B);
+
+        Install a2 = Install.single(TestApp.A2).setStaged()
+                .setEnableRollback(PackageManager.RollbackDataPolicy.WIPE);
+        Install b2 = Install.single(TestApp.B2).setStaged()
+                .setEnableRollback(PackageManager.RollbackDataPolicy.RESTORE);
+        Install.multi(a2, b2).setEnableRollback().setStaged().commit();
+    }
+
+    @Test
+    public void testRollbackDataPolicy_Phase2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
+        // Write user data version = 2
+        InstallUtils.processUserData(TestApp.A);
+        InstallUtils.processUserData(TestApp.B);
+
+        RollbackInfo info = RollbackUtils.getAvailableRollback(TestApp.A);
+        RollbackUtils.rollback(info.getRollbackId());
+    }
+
+    @Test
+    public void testRollbackDataPolicy_Phase3() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+        assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(1);
+        // Read user data version from userdata.txt
+        // A's user data version is -1 for user data is wiped.
+        // B's user data version is 1 as rollback committed.
+        assertThat(InstallUtils.getUserDataVersion(TestApp.A)).isEqualTo(-1);
+        assertThat(InstallUtils.getUserDataVersion(TestApp.B)).isEqualTo(1);
+    }
+
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 8ab4c4c..4644d8a 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -192,6 +192,15 @@
         }
     }
 
+    @Test
+    public void testRollbackDataPolicy() throws Exception {
+        runPhase("testRollbackDataPolicy_Phase1");
+        getDevice().reboot();
+        runPhase("testRollbackDataPolicy_Phase2");
+        getDevice().reboot();
+        runPhase("testRollbackDataPolicy_Phase3");
+    }
+
     private void crashProcess(String processName, int numberOfCrashes) throws Exception {
         String pid = "";
         String lastPid = "invalid";
diff --git a/tests/TelephonyCommonTests/Android.bp b/tests/TelephonyCommonTests/Android.bp
new file mode 100644
index 0000000..4f7569d
--- /dev/null
+++ b/tests/TelephonyCommonTests/Android.bp
@@ -0,0 +1,49 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "TelephonyCommonTests",
+    srcs: [
+        ":framework-telephony-common-sources",
+        "**/*.java",
+    ],
+    static_libs: [
+        "mockito-target-extended",
+        "androidx.test.rules",
+        "truth-prebuilt",
+        "platform-test-annotations",
+        "androidx.core_core",
+        "androidx.fragment_fragment",
+        "androidx.test.ext.junit"
+    ],
+
+    jni_libs: ["libdexmakerjvmtiagent"],
+
+    // We need to rename SmsApplication to the test package or else it'll get clobbered by the
+    // hidden api checker
+    jarjar_rules: "jarjar-rules.txt",
+
+    // Uncomment this and comment out the jarjar rule if you want to attach a debugger and step
+    // through the renamed classes.
+    // platform_apis: true,
+
+    libs: [
+        "android.test.runner",
+        "android.test.mock",
+        "android.test.base",
+        "unsupportedappusage",
+    ],
+}
diff --git a/tests/TelephonyCommonTests/AndroidManifest.xml b/tests/TelephonyCommonTests/AndroidManifest.xml
new file mode 100644
index 0000000..63f38c6
--- /dev/null
+++ b/tests/TelephonyCommonTests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.internal.telephony.tests"
+          android:debuggable="true">
+
+    <application android:label="TelephonyCommonTests"
+                 android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.internal.telephony.tests"
+                     android:label="Telephony common tests"
+                     android:debuggable="true"/>
+</manifest>
diff --git a/tests/TelephonyCommonTests/AndroidTest.xml b/tests/TelephonyCommonTests/AndroidTest.xml
new file mode 100644
index 0000000..e9fdabc
--- /dev/null
+++ b/tests/TelephonyCommonTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<configuration description="Runs Telephony Common Test Cases.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="TelephonyCommonTests.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="TelephonyCommonTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.internal.telephony.tests" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+    </test>
+</configuration>
diff --git a/tests/TelephonyCommonTests/jarjar-rules.txt b/tests/TelephonyCommonTests/jarjar-rules.txt
new file mode 100644
index 0000000..4d1115f
--- /dev/null
+++ b/tests/TelephonyCommonTests/jarjar-rules.txt
@@ -0,0 +1,3 @@
+rule com.android.internal.telephony.SmsApplication* com.android.internal.telephony.tests.SmsApplication@1
+rule android.telephony.PackageChangeReceiver* com.android.internal.telephony.tests.PackageChangeReceiver@1
+rule com.android.internal.os.BackgroundThread* com.android.internal.telephony.tests.BackgroundThread@1
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
new file mode 100644
index 0000000..83fd208
--- /dev/null
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNotNull;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.app.AppOpsManager;
+import android.app.role.RoleManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Telephony;
+import android.telephony.TelephonyManager;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.telephony.SmsApplication;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Unit tests for the {@link SmsApplication} utility class
+ */
+@RunWith(AndroidJUnit4.class)
+public class SmsApplicationTest {
+    private static final ComponentName TEST_COMPONENT_NAME =
+            ComponentName.unflattenFromString("com.android.test/.TestSmsApp");
+    private static final String MMS_RECEIVER_NAME = "TestMmsReceiver";
+    private static final String RESPOND_VIA_SMS_NAME = "TestRespondViaSmsHandler";
+    private static final String SEND_TO_NAME = "TestSendTo";
+    private static final int SMS_APP_UID = 10001;
+
+    private static final int FAKE_PHONE_UID = 10002;
+    private static final int FAKE_MMS_UID = 10003;
+    private static final int FAKE_BT_UID = 10004;
+    private static final int FAKE_TELEPHONY_PROVIDER_UID = 10005;
+
+    private static final String[] APP_OPS_TO_CHECK = {
+            AppOpsManager.OPSTR_READ_SMS,
+            AppOpsManager.OPSTR_WRITE_SMS,
+            AppOpsManager.OPSTR_RECEIVE_SMS,
+            AppOpsManager.OPSTR_RECEIVE_WAP_PUSH,
+            AppOpsManager.OPSTR_SEND_SMS,
+            AppOpsManager.OPSTR_READ_CELL_BROADCASTS
+    };
+
+    private static final Set<String> SCHEMES_FOR_PREFERRED_APP = Arrays.stream(new String[]{
+            "mms",
+            "mmsto",
+            "sms",
+            "smsto"
+    }).collect(Collectors.toSet());
+
+    @Mock private Context mContext;
+    @Mock private TelephonyManager mTelephonyManager;
+    @Mock private RoleManager mRoleManager;
+    @Mock private PackageManager mPackageManager;
+    @Mock private AppOpsManager mAppOpsManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+        when(mContext.getSystemService(Context.ROLE_SERVICE)).thenReturn(mRoleManager);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getSystemService(RoleManager.class)).thenReturn(mRoleManager);
+        when(mContext.getSystemService(AppOpsManager.class)).thenReturn(mAppOpsManager);
+        when(mContext.createContextAsUser(isNotNull(), anyInt())).thenReturn(mContext);
+
+        doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+                .when(mPackageManager)
+                .queryBroadcastReceiversAsUser(nullable(Intent.class), anyInt(),
+                        nullable(UserHandle.class));
+        doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+                .when(mPackageManager)
+                .queryIntentActivitiesAsUser(nullable(Intent.class), anyInt(),
+                        nullable(UserHandle.class));
+        doAnswer(invocation -> getResolveInfosForIntent(invocation.getArgument(0)))
+                .when(mPackageManager)
+                .queryIntentServicesAsUser(nullable(Intent.class), anyInt(),
+                        nullable(UserHandle.class));
+
+        when(mTelephonyManager.isSmsCapable()).thenReturn(true);
+        when(mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)).thenReturn(true);
+        when(mRoleManager.getDefaultSmsPackage(anyInt()))
+                .thenReturn(TEST_COMPONENT_NAME.getPackageName());
+
+        for (String opStr : APP_OPS_TO_CHECK) {
+            when(mAppOpsManager.unsafeCheckOp(
+                    opStr, SMS_APP_UID, TEST_COMPONENT_NAME.getPackageName()))
+                    .thenReturn(AppOpsManager.MODE_ALLOWED);
+        }
+    }
+
+    @Test
+    public void testGetDefaultSmsApplication() {
+        assertEquals(TEST_COMPONENT_NAME,
+                SmsApplication.getDefaultSmsApplicationAsUser(mContext, false, 0));
+    }
+
+    @Test
+    public void testGetDefaultSmsApplicationWithAppOpsFix() throws Exception {
+        when(mAppOpsManager.unsafeCheckOp(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
+                TEST_COMPONENT_NAME.getPackageName()))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
+        setupPackageInfosForCoreApps();
+
+        assertEquals(TEST_COMPONENT_NAME,
+                SmsApplication.getDefaultSmsApplicationAsUser(mContext, true, 0));
+        verify(mAppOpsManager, atLeastOnce()).setUidMode(AppOpsManager.OPSTR_READ_SMS, SMS_APP_UID,
+                AppOpsManager.MODE_ALLOWED);
+    }
+
+    @Test
+    public void testPackageChanged() throws Exception {
+        setupPackageInfosForCoreApps();
+        SmsApplication.initSmsPackageMonitor(mContext);
+        verify(mContext).createContextAsUser(eq(UserHandle.ALL), anyInt());
+        ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext).registerReceiver(captor.capture(), isNotNull(),
+                isNull(), nullable(Handler.class));
+        BroadcastReceiver smsPackageMonitor = captor.getValue();
+
+        Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
+        packageChangedIntent.setData(
+                Uri.fromParts("package", TEST_COMPONENT_NAME.getPackageName(), null));
+        smsPackageMonitor.onReceive(mContext, packageChangedIntent);
+
+        ArgumentCaptor<IntentFilter> intentFilterCaptor =
+                ArgumentCaptor.forClass(IntentFilter.class);
+        verify(mPackageManager, times(SCHEMES_FOR_PREFERRED_APP.size()))
+                .replacePreferredActivity(intentFilterCaptor.capture(),
+                        eq(IntentFilter.MATCH_CATEGORY_SCHEME
+                                | IntentFilter.MATCH_ADJUSTMENT_NORMAL),
+                        isNotNull(List.class),
+                        eq(new ComponentName(TEST_COMPONENT_NAME.getPackageName(), SEND_TO_NAME)));
+
+        Set<String> capturedSchemes = intentFilterCaptor.getAllValues().stream()
+                .map(intentFilter -> intentFilter.getDataScheme(0))
+                .collect(Collectors.toSet());
+        assertEquals(SCHEMES_FOR_PREFERRED_APP.size(), capturedSchemes.size());
+        assertTrue(SCHEMES_FOR_PREFERRED_APP.containsAll(capturedSchemes));
+    }
+
+    private void setupPackageInfosForCoreApps() throws Exception {
+        PackageInfo phonePackageInfo = new PackageInfo();
+        ApplicationInfo phoneApplicationInfo = new ApplicationInfo();
+        phoneApplicationInfo.uid = FAKE_PHONE_UID;
+        phonePackageInfo.applicationInfo = phoneApplicationInfo;
+        when(mPackageManager.getPackageInfo(eq(SmsApplication.PHONE_PACKAGE_NAME), anyInt()))
+                .thenReturn(phonePackageInfo);
+
+        PackageInfo mmsPackageInfo = new PackageInfo();
+        ApplicationInfo mmsApplicationInfo = new ApplicationInfo();
+        mmsApplicationInfo.uid = FAKE_MMS_UID;
+        mmsPackageInfo.applicationInfo = mmsApplicationInfo;
+        when(mPackageManager.getPackageInfo(eq(SmsApplication.MMS_SERVICE_PACKAGE_NAME), anyInt()))
+                .thenReturn(mmsPackageInfo);
+
+        PackageInfo bluetoothPackageInfo = new PackageInfo();
+        ApplicationInfo bluetoothApplicationInfo = new ApplicationInfo();
+        bluetoothApplicationInfo.uid = FAKE_BT_UID;
+        bluetoothPackageInfo.applicationInfo = bluetoothApplicationInfo;
+        when(mPackageManager.getPackageInfo(eq(SmsApplication.BLUETOOTH_PACKAGE_NAME), anyInt()))
+                .thenReturn(bluetoothPackageInfo);
+
+        PackageInfo telephonyProviderPackageInfo = new PackageInfo();
+        ApplicationInfo telephonyProviderApplicationInfo = new ApplicationInfo();
+        telephonyProviderApplicationInfo.uid = FAKE_TELEPHONY_PROVIDER_UID;
+        telephonyProviderPackageInfo.applicationInfo = telephonyProviderApplicationInfo;
+        when(mPackageManager.getPackageInfo(
+                eq(SmsApplication.TELEPHONY_PROVIDER_PACKAGE_NAME), anyInt()))
+                .thenReturn(telephonyProviderPackageInfo);
+    }
+
+    private List<ResolveInfo> getResolveInfosForIntent(Intent intent) {
+        switch (intent.getAction()) {
+            case Telephony.Sms.Intents.SMS_DELIVER_ACTION:
+                return Collections.singletonList(makeSmsDeliverResolveInfo());
+            case Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION:
+                return Collections.singletonList(makeWapPushResolveInfo());
+            case TelephonyManager.ACTION_RESPOND_VIA_MESSAGE:
+                return Collections.singletonList(makeRespondViaMessageResolveInfo());
+            case Intent.ACTION_SENDTO:
+                return Collections.singletonList(makeSendToResolveInfo());
+        }
+        return Collections.emptyList();
+    }
+
+    private ApplicationInfo makeSmsApplicationInfo() {
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.uid = SMS_APP_UID;
+        return applicationInfo;
+    }
+
+    private ResolveInfo makeSmsDeliverResolveInfo() {
+        ResolveInfo info = new ResolveInfo();
+        ActivityInfo activityInfo = new ActivityInfo();
+        activityInfo.applicationInfo = makeSmsApplicationInfo();
+
+        activityInfo.permission = Manifest.permission.BROADCAST_SMS;
+        activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+        activityInfo.name = TEST_COMPONENT_NAME.getClassName();
+
+        info.activityInfo = activityInfo;
+        return info;
+    }
+
+    private ResolveInfo makeWapPushResolveInfo() {
+        ResolveInfo info = new ResolveInfo();
+        ActivityInfo activityInfo = new ActivityInfo();
+
+        activityInfo.permission = Manifest.permission.BROADCAST_WAP_PUSH;
+        activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+        activityInfo.name = MMS_RECEIVER_NAME;
+
+        info.activityInfo = activityInfo;
+        return info;
+    }
+
+    private ResolveInfo makeRespondViaMessageResolveInfo() {
+        ResolveInfo info = new ResolveInfo();
+        ServiceInfo serviceInfo = new ServiceInfo();
+
+        serviceInfo.permission = Manifest.permission.SEND_RESPOND_VIA_MESSAGE;
+        serviceInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+        serviceInfo.name = RESPOND_VIA_SMS_NAME;
+
+        info.serviceInfo = serviceInfo;
+        return info;
+    }
+
+    private ResolveInfo makeSendToResolveInfo() {
+        ResolveInfo info = new ResolveInfo();
+        ActivityInfo activityInfo = new ActivityInfo();
+
+        activityInfo.packageName = TEST_COMPONENT_NAME.getPackageName();
+        activityInfo.name = SEND_TO_NAME;
+
+        info.activityInfo = activityInfo;
+        return info;
+    }
+}
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
new file mode 100644
index 0000000..a7853b6
--- /dev/null
+++ b/tests/net/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "FrameworksNetIntegrationTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index a7eef05..a7328ac 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -38,6 +38,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -65,6 +66,7 @@
     private static final InetAddress GATEWAY62 = address("fe80::6:22%lo");
     private static final InetAddress TESTIPV4ADDR = address("192.168.47.42");
     private static final InetAddress TESTIPV6ADDR = address("fe80::7:33%43");
+    private static final Inet4Address DHCPSERVER = (Inet4Address) address("192.0.2.1");
     private static final String NAME = "qmi0";
     private static final String DOMAINS = "google.com";
     private static final String PRIV_DNS_SERVER_NAME = "private.dns.com";
@@ -93,6 +95,7 @@
         assertNull(lp.getHttpProxy());
         assertNull(lp.getTcpBufferSizes());
         assertNull(lp.getNat64Prefix());
+        assertNull(lp.getDhcpServerAddress());
         assertFalse(lp.isProvisioned());
         assertFalse(lp.isIpv4Provisioned());
         assertFalse(lp.isIpv6Provisioned());
@@ -119,6 +122,7 @@
         lp.setMtu(MTU);
         lp.setTcpBufferSizes(TCP_BUFFER_SIZES);
         lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
+        lp.setDhcpServerAddress(DHCPSERVER);
         lp.setWakeOnLanSupported(true);
         return lp;
     }
@@ -960,11 +964,13 @@
 
         source.setWakeOnLanSupported(true);
 
+        source.setDhcpServerAddress((Inet4Address) GATEWAY1);
+
         final LinkProperties stacked = new LinkProperties();
         stacked.setInterfaceName("test-stacked");
         source.addStackedLink(stacked);
 
-        assertParcelSane(source, 15 /* fieldCount */);
+        assertParcelSane(source, 16 /* fieldCount */);
     }
 
     @Test
@@ -1091,6 +1097,15 @@
     }
 
     @Test
+    public void testDhcpServerAddress() {
+        final LinkProperties lp = makeTestObject();
+        assertEquals(DHCPSERVER, lp.getDhcpServerAddress());
+
+        lp.clear();
+        assertNull(lp.getDhcpServerAddress());
+    }
+
+    @Test
     public void testWakeOnLanSupported() {
         final LinkProperties lp = makeTestObject();
         assertTrue(lp.isWakeOnLanSupported());
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 7d9b7b7..874bd4b 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -36,6 +36,7 @@
         "services.net",
         "testables",
     ],
+    test_suites: ["device-tests"],
     use_embedded_native_libs: true,
     jni_libs: [
         // For mockito extended
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index 4dd3b5a..09c0e48 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -28,7 +28,9 @@
     <!-- Reading network status -->
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+    <uses-permission android:name="android.permission.NETWORK_FACTORY" />
+    <uses-permission android:name="android.permission.NETWORK_STACK" />
+    <uses-permission android:name="android.permission.OBSERVE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <!-- Reading DeviceConfig flags -->
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index c50229a..d1d6a26 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -16,12 +16,12 @@
 
 package com.android.framework.permission.tests;
 
-import android.media.AudioAttributes;
 import android.os.Binder;
 import android.os.IVibratorService;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.VibrationAttributes;
 import android.os.VibrationEffect;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -52,8 +52,8 @@
         try {
             final VibrationEffect effect =
                     VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE);
-            final AudioAttributes attrs = new AudioAttributes.Builder()
-                    .setUsage(AudioAttributes.USAGE_ALARM)
+            final VibrationAttributes attrs = new VibrationAttributes.Builder()
+                    .setUsage(VibrationAttributes.USAGE_ALARM)
                     .build();
             mVibratorService.vibrate(Process.myUid(), null, effect, attrs,
                     "testVibrate", new Binder());
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index c557656..74e2a098 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -1641,8 +1641,14 @@
                                            ParsedResource* out_resource) {
   out_resource->name.type = ResourceType::kStyleable;
 
-  // Declare-styleable is kPrivate by default, because it technically only exists in R.java.
-  out_resource->visibility_level = Visibility::Level::kPublic;
+  if (!options_.preserve_visibility_of_styleables) {
+    // This was added in change Idd21b5de4d20be06c6f8c8eb5a22ccd68afc4927 to mimic aapt1, but no one
+    // knows exactly what for.
+    //
+    // FWIW, styleables only appear in generated R classes.  For custom views these should always be
+    // package-private (to be used only by the view class); themes are a different story.
+    out_resource->visibility_level = Visibility::Level::kPublic;
+  }
 
   // Declare-styleable only ends up in default config;
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index 06bb0c9..9d3ecc8 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -46,6 +46,12 @@
    */
   bool error_on_positional_arguments = true;
 
+  /**
+   * If true, apply the same visibility rules for styleables as are used for
+   * all other resources.  Otherwise, all styleables will be made public.
+   */
+  bool preserve_visibility_of_styleables = false;
+
   // If visibility was forced, we need to use it when creating a new resource and also error if we
   // try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
   Maybe<Visibility::Level> visibility;
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 4237469..24531bc 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -614,6 +614,32 @@
   EXPECT_THAT(styleable->entries[2].name, Eq(make_value(test::ParseNameOrDie("attr/baz"))));
 }
 
+TEST_F(ResourceParserTest, ParseDeclareStyleablePreservingVisibility) {
+  StringInputStream input(R"(
+      <resources>
+        <declare-styleable name="foo">
+          <attr name="myattr" />
+        </declare-styleable>
+        <declare-styleable name="bar">
+          <attr name="myattr" />
+        </declare-styleable>
+        <public type="styleable" name="bar" />
+      </resources>)");
+  ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"},
+                        ConfigDescription::DefaultConfig(),
+                        ResourceParserOptions{.preserve_visibility_of_styleables = true});
+
+  xml::XmlPullParser xml_parser(&input);
+  ASSERT_TRUE(parser.Parse(&xml_parser));
+
+  EXPECT_EQ(
+      table_.FindResource(test::ParseNameOrDie("styleable/foo")).value().entry->visibility.level,
+      Visibility::Level::kUndefined);
+  EXPECT_EQ(
+      table_.FindResource(test::ParseNameOrDie("styleable/bar")).value().entry->visibility.level,
+      Visibility::Level::kPublic);
+}
+
 TEST_F(ResourceParserTest, ParsePrivateAttributesDeclareStyleable) {
   std::string input = R"(
       <declare-styleable xmlns:privAndroid="http://schemas.android.com/apk/prv/res/android"
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index d50b1de..3268653 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -159,6 +159,7 @@
 
     ResourceParserOptions parser_options;
     parser_options.error_on_positional_arguments = !options.legacy_mode;
+    parser_options.preserve_visibility_of_styleables = options.preserve_visibility_of_styleables;
     parser_options.translatable = translatable_file;
 
     // If visibility was forced, we need to use it when creating a new resource and also error if
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index d3456b2..1752a1a 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -35,6 +35,8 @@
   bool pseudolocalize = false;
   bool no_png_crunch = false;
   bool legacy_mode = false;
+  // See comments on aapt::ResourceParserOptions.
+  bool preserve_visibility_of_styleables = false;
   bool verbose = false;
 };
 
@@ -56,6 +58,11 @@
     AddOptionalSwitch("--no-crunch", "Disables PNG processing", &options_.no_png_crunch);
     AddOptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
         &options_.legacy_mode);
+    AddOptionalSwitch("--preserve-visibility-of-styleables",
+                      "If specified, apply the same visibility rules for\n"
+                      "styleables as are used for all other resources.\n"
+                      "Otherwise, all stylesables will be made public.",
+                      &options_.preserve_visibility_of_styleables);
     AddOptionalFlag("--visibility",
         "Sets the visibility of the compiled resources to the specified\n"
             "level. Accepted levels: public, private, default", &visibility_);
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index b725920..27960c8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -365,6 +365,8 @@
   });
   manifest_action["instrumentation"]["meta-data"] = meta_data_action;
 
+  manifest_action["feature"];
+  manifest_action["feature"]["inherit-from"];
   manifest_action["original-package"];
   manifest_action["overlay"];
   manifest_action["protected-broadcast"];
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 15c3278..d3958a6 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -25,6 +25,8 @@
         "java_writer.cpp",
         "java_writer_q.cpp",
         "main.cpp",
+        "native_writer.cpp",
+        "native_writer_q.cpp",
         "utils.cpp",
     ],
     cflags: [
@@ -121,17 +123,5 @@
         "libcutils",
     ],
     static_libs: ["libstatssocket"],
-    target: {
-        android: {
-            shared_libs: [
-                "libutils",
-            ],
-        },
-        host: {
-            static_libs: [
-                "libutils",
-            ],
-        },
-    },
 }
 
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index fa55601..0b82a3d 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -405,9 +405,9 @@
         atomDecl.whitelisted = true;
     }
 
-    if (atomField->options().HasExtension(os::statsd::log_from_module)) {
+    if (atomField->options().HasExtension(os::statsd::module)) {
         atomDecl.hasModule = true;
-        atomDecl.moduleName = atomField->options().GetExtension(os::statsd::log_from_module);
+        atomDecl.moduleName = atomField->options().GetExtension(os::statsd::module);
     }
 
     vector<java_type_t> signature;
diff --git a/tools/stats_log_api_gen/atoms_info_writer.h b/tools/stats_log_api_gen/atoms_info_writer.h
index bc67782..12ac862 100644
--- a/tools/stats_log_api_gen/atoms_info_writer.h
+++ b/tools/stats_log_api_gen/atoms_info_writer.h
@@ -27,8 +27,7 @@
 using namespace std;
 
 int write_atoms_info_cpp(FILE* out, const Atoms& atoms, const string& namespaceStr,
-        const string& importHeader, const string& statslogHeader
-);
+        const string& importHeader, const string& statslogHeader);
 
 int write_atoms_info_header(FILE* out, const Atoms& atoms, const string& namespaceStr);
 
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index 712b48e..7f0872c 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -48,7 +48,8 @@
         FILE* out,
         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
         const AtomDecl &attributionDecl,
-        const string& moduleName
+        const string& moduleName,
+        const bool supportQ
         ) {
     for (auto signature_to_modules_it = signatures_to_modules.begin();
             signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
@@ -82,8 +83,10 @@
 
         // Print method body.
         string indent("");
-        if (DEFAULT_MODULE_NAME != moduleName) {
-            fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {\n");
+        if (supportQ) {
+            // TODO(b/146235828): Use just SDK_INT check once it is incremented from Q.
+            fprintf(out, "        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q ||\n");
+            fprintf(out, "                Build.VERSION.CODENAME.equals(\"R\")) {\n");
             indent = "    ";
         }
 
@@ -193,7 +196,7 @@
         fprintf(out, "%s        StatsLog.write(builder.build());\n", indent.c_str());
 
         // Add support for writing using Q schema if this is not the default module.
-        if (DEFAULT_MODULE_NAME != moduleName) {
+        if (supportQ) {
             fprintf(out, "        } else {\n");
             fprintf(out, "            QLogger.write(code");
             argIndex = 1;
@@ -225,15 +228,17 @@
 
 int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
                                     const string& moduleName, const string& javaClass,
-                                    const string& javaPackage) {
+                                    const string& javaPackage, const bool supportQ) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
     fprintf(out, "package %s;\n", javaPackage.c_str());
     fprintf(out, "\n");
     fprintf(out, "\n");
-    fprintf(out, "import android.os.Build;\n");
-    fprintf(out, "import android.os.SystemClock;\n");
+    if (supportQ) {
+        fprintf(out, "import android.os.Build;\n");
+        fprintf(out, "import android.os.SystemClock;\n");
+    }
 
     if (DEFAULT_MODULE_NAME == moduleName) {
         // Mainline modules don't use WorkSource logging.
@@ -271,12 +276,15 @@
 
     // Print write methods.
     fprintf(out, "    // Write methods\n");
-    errors += write_java_methods(out, atoms.signatures_to_modules, attributionDecl, moduleName);
+    errors += write_java_methods(
+            out, atoms.signatures_to_modules, attributionDecl, moduleName, supportQ);
     errors += write_java_non_chained_methods(
             out, atoms.non_chained_signatures_to_modules, moduleName);
     if (DEFAULT_MODULE_NAME == moduleName) {
         errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName);
-    } else {
+    }
+
+    if (supportQ) {
         errors += write_java_q_logger_class(
                 out, atoms.signatures_to_modules, attributionDecl, moduleName);
     }
diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h
index 031266b..9324b23 100644
--- a/tools/stats_log_api_gen/java_writer.h
+++ b/tools/stats_log_api_gen/java_writer.h
@@ -32,8 +32,7 @@
 
 int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
                                     const string& moduleName, const string& javaClass,
-                                    const string& javaPackage
-);
+                                    const string& javaPackage, const bool supportQ);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
index c8f4ccf..96ac745 100644
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ b/tools/stats_log_api_gen/java_writer_q.h
@@ -37,22 +37,20 @@
         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
         const AtomDecl &attributionDecl,
         const string& moduleName,
-        const string& indent
-);
+        const string& indent);
 
 void write_java_helpers_for_q_schema_methods(
         FILE * out,
         const AtomDecl &attributionDecl,
         const int requiredHelpers,
-        const string& indent
-);
+        const string& indent);
 
 #if defined(STATS_SCHEMA_LEGACY)
 int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl);
 
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-                                    const string& moduleName, const string& javaClass,
-                                    const string& javaPackage);
+int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
+        const AtomDecl &attributionDecl, const string& moduleName, const string& javaClass,
+        const string& javaPackage);
 #endif
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index ad171da..00a3704 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -6,6 +6,7 @@
 #include "java_writer.h"
 #endif
 #include "java_writer_q.h"
+#include "native_writer.h"
 #include "utils.h"
 
 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
@@ -27,496 +28,6 @@
 
 using android::os::statsd::Atom;
 
-static int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
-                               const string& moduleName, const string& cppNamespace,
-                               const string& importHeader) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-
-    fprintf(out, "#include <mutex>\n");
-    fprintf(out, "#include <chrono>\n");
-    fprintf(out, "#include <thread>\n");
-    fprintf(out, "#ifdef __ANDROID__\n");
-    fprintf(out, "#include <cutils/properties.h>\n");
-    fprintf(out, "#endif\n");
-    fprintf(out, "#include <stats_event_list.h>\n");
-    fprintf(out, "#include <log/log.h>\n");
-    fprintf(out, "#include <%s>\n", importHeader.c_str());
-    fprintf(out, "#include <utils/SystemClock.h>\n");
-    fprintf(out, "\n");
-
-    write_namespace(out, cppNamespace);
-    fprintf(out, "// the single event tag id for all stats logs\n");
-    fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
-    fprintf(out, "#ifdef __ANDROID__\n");
-    fprintf(out, "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n");
-    fprintf(out, "#else\n");
-    fprintf(out, "const static bool kStatsdEnabled = false;\n");
-    fprintf(out, "#endif\n");
-
-    fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
-    fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
-    fprintf(out, "static std::mutex mLogdRetryMutex;\n");
-
-    // Print write methods
-    fprintf(out, "\n");
-    for (auto signature_to_modules_it = atoms.signatures_to_modules.begin();
-        signature_to_modules_it != atoms.signatures_to_modules.end(); signature_to_modules_it++) {
-        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
-            continue;
-        }
-        vector<java_type_t> signature = signature_to_modules_it->first;
-        int argIndex;
-
-        fprintf(out, "int\n");
-        fprintf(out, "try_stats_write(int32_t code");
-        argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                for (auto chainField : attributionDecl.fields) {
-                    if (chainField.javaType == JAVA_TYPE_STRING) {
-                            fprintf(out, ", const std::vector<%s>& %s",
-                                 cpp_type_name(chainField.javaType),
-                                 chainField.name.c_str());
-                    } else {
-                            fprintf(out, ", const %s* %s, size_t %s_length",
-                                 cpp_type_name(chainField.javaType),
-                                 chainField.name.c_str(), chainField.name.c_str());
-                    }
-                }
-            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
-                             "const std::map<int, int64_t>& arg%d_2, "
-                             "const std::map<int, char const*>& arg%d_3, "
-                             "const std::map<int, float>& arg%d_4",
-                             argIndex, argIndex, argIndex, argIndex);
-            } else {
-                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ")\n");
-
-        fprintf(out, "{\n");
-        argIndex = 1;
-        fprintf(out, "  if (kStatsdEnabled) {\n");
-        fprintf(out, "    stats_event_list event(kStatsEventTag);\n");
-        fprintf(out, "    event << android::elapsedRealtimeNano();\n\n");
-        fprintf(out, "    event << code;\n\n");
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                for (const auto &chainField : attributionDecl.fields) {
-                    if (chainField.javaType == JAVA_TYPE_STRING) {
-                        fprintf(out, "    if (%s_length != %s.size()) {\n",
-                            attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
-                        fprintf(out, "        return -EINVAL;\n");
-                        fprintf(out, "    }\n");
-                    }
-                }
-                fprintf(out, "\n    event.begin();\n");
-                fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
-                    attributionDecl.fields.front().name.c_str());
-                fprintf(out, "        event.begin();\n");
-                for (const auto &chainField : attributionDecl.fields) {
-                    if (chainField.javaType == JAVA_TYPE_STRING) {
-                        fprintf(out, "        if (%s[i] != NULL) {\n", chainField.name.c_str());
-                        fprintf(out, "           event << %s[i];\n", chainField.name.c_str());
-                        fprintf(out, "        } else {\n");
-                        fprintf(out, "           event << \"\";\n");
-                        fprintf(out, "        }\n");
-                    } else {
-                        fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
-                    }
-                }
-                fprintf(out, "        event.end();\n");
-                fprintf(out, "    }\n");
-                fprintf(out, "    event.end();\n\n");
-            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                    fprintf(out, "    event.begin();\n\n");
-                    fprintf(out, "    for (const auto& it : arg%d_1) {\n", argIndex);
-                    fprintf(out, "         event.begin();\n");
-                    fprintf(out, "         event << it.first;\n");
-                    fprintf(out, "         event << it.second;\n");
-                    fprintf(out, "         event.end();\n");
-                    fprintf(out, "    }\n");
-
-                    fprintf(out, "    for (const auto& it : arg%d_2) {\n", argIndex);
-                    fprintf(out, "         event.begin();\n");
-                    fprintf(out, "         event << it.first;\n");
-                    fprintf(out, "         event << it.second;\n");
-                    fprintf(out, "         event.end();\n");
-                    fprintf(out, "    }\n");
-
-                    fprintf(out, "    for (const auto& it : arg%d_3) {\n", argIndex);
-                    fprintf(out, "         event.begin();\n");
-                    fprintf(out, "         event << it.first;\n");
-                    fprintf(out, "         event << it.second;\n");
-                    fprintf(out, "         event.end();\n");
-                    fprintf(out, "    }\n");
-
-                    fprintf(out, "    for (const auto& it : arg%d_4) {\n", argIndex);
-                    fprintf(out, "         event.begin();\n");
-                    fprintf(out, "         event << it.first;\n");
-                    fprintf(out, "         event << it.second;\n");
-                    fprintf(out, "         event.end();\n");
-                    fprintf(out, "    }\n");
-
-                    fprintf(out, "    event.end();\n\n");
-            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-                fprintf(out,
-                        "    event.AppendCharArray(arg%d.arg, "
-                        "arg%d.arg_length);\n",
-                        argIndex, argIndex);
-            } else {
-                if (*arg == JAVA_TYPE_STRING) {
-                    fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
-                    fprintf(out, "        arg%d = \"\";\n", argIndex);
-                    fprintf(out, "    }\n");
-                }
-                fprintf(out, "    event << arg%d;\n", argIndex);
-            }
-            argIndex++;
-        }
-
-        fprintf(out, "    return event.write(LOG_ID_STATS);\n");
-        fprintf(out, "  } else {\n");
-        fprintf(out, "    return 1;\n");
-        fprintf(out, "  }\n");
-        fprintf(out, "}\n");
-        fprintf(out, "\n");
-    }
-
-   for (auto signature_to_modules_it = atoms.signatures_to_modules.begin();
-       signature_to_modules_it != atoms.signatures_to_modules.end(); signature_to_modules_it++) {
-       if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
-           continue;
-       }
-       vector<java_type_t> signature = signature_to_modules_it->first;
-       int argIndex;
-
-       fprintf(out, "int\n");
-       fprintf(out, "stats_write(int32_t code");
-       argIndex = 1;
-       for (vector<java_type_t>::const_iterator arg = signature.begin();
-           arg != signature.end(); arg++) {
-           if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-               for (auto chainField : attributionDecl.fields) {
-                   if (chainField.javaType == JAVA_TYPE_STRING) {
-                           fprintf(out, ", const std::vector<%s>& %s",
-                                cpp_type_name(chainField.javaType),
-                                chainField.name.c_str());
-                   } else {
-                           fprintf(out, ", const %s* %s, size_t %s_length",
-                                cpp_type_name(chainField.javaType),
-                                chainField.name.c_str(), chainField.name.c_str());
-                   }
-               }
-           } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-               fprintf(out,
-                       ", const std::map<int, int32_t>& arg%d_1, "
-                       "const std::map<int, int64_t>& arg%d_2, "
-                       "const std::map<int, char const*>& arg%d_3, "
-                       "const std::map<int, float>& arg%d_4",
-                       argIndex, argIndex, argIndex, argIndex);
-           } else {
-               fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
-           }
-           argIndex++;
-       }
-       fprintf(out, ")\n");
-
-       fprintf(out, "{\n");
-       fprintf(out, "  int ret = 0;\n");
-
-       fprintf(out, "  for(int retry = 0; retry < 2; ++retry) {\n");
-       fprintf(out, "      ret =  try_stats_write(code");
-
-       argIndex = 1;
-       for (vector<java_type_t>::const_iterator arg = signature.begin();
-           arg != signature.end(); arg++) {
-           if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-               for (auto chainField : attributionDecl.fields) {
-                   if (chainField.javaType == JAVA_TYPE_STRING) {
-                           fprintf(out, ", %s",
-                                chainField.name.c_str());
-                   } else {
-                           fprintf(out, ",  %s,  %s_length",
-                                chainField.name.c_str(), chainField.name.c_str());
-                   }
-               }
-           } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-               fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
-                       argIndex, argIndex, argIndex);
-           } else {
-               fprintf(out, ", arg%d", argIndex);
-           }
-           argIndex++;
-       }
-       fprintf(out, ");\n");
-       fprintf(out, "      if (ret >= 0) { break; }\n");
-
-       fprintf(out, "      {\n");
-       fprintf(out, "          std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
-       fprintf(out, "          if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
-                                "kMinRetryIntervalNs) break;\n");
-       fprintf(out, "          lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
-       fprintf(out, "      }\n");
-       fprintf(out, "      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
-       fprintf(out, "  }\n");
-       fprintf(out, "  if (ret < 0) {\n");
-       fprintf(out, "      note_log_drop(ret, code);\n");
-       fprintf(out, "  }\n");
-       fprintf(out, "  return ret;\n");
-       fprintf(out, "}\n");
-       fprintf(out, "\n");
-   }
-
-    for (auto signature_it = atoms.non_chained_signatures_to_modules.begin();
-            signature_it != atoms.non_chained_signatures_to_modules.end(); signature_it++) {
-        if (!signature_needed_for_module(signature_it->second, moduleName)) {
-            continue;
-        }
-        vector<java_type_t> signature = signature_it->first;
-        int argIndex;
-
-        fprintf(out, "int\n");
-        fprintf(out, "try_stats_write_non_chained(int32_t code");
-        argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
-            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
-            argIndex++;
-        }
-        fprintf(out, ")\n");
-
-        fprintf(out, "{\n");
-        argIndex = 1;
-        fprintf(out, "  if (kStatsdEnabled) {\n");
-        fprintf(out, "    stats_event_list event(kStatsEventTag);\n");
-        fprintf(out, "    event << android::elapsedRealtimeNano();\n\n");
-        fprintf(out, "    event << code;\n\n");
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
-            if (argIndex == 1) {
-                fprintf(out, "    event.begin();\n\n");
-                fprintf(out, "    event.begin();\n");
-            }
-            if (*arg == JAVA_TYPE_STRING) {
-                fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
-                fprintf(out, "        arg%d = \"\";\n", argIndex);
-                fprintf(out, "    }\n");
-            }
-            if (*arg == JAVA_TYPE_BYTE_ARRAY) {
-                fprintf(out,
-                        "    event.AppendCharArray(arg%d.arg, "
-                        "arg%d.arg_length);",
-                        argIndex, argIndex);
-            } else {
-                fprintf(out, "    event << arg%d;\n", argIndex);
-            }
-            if (argIndex == 2) {
-                fprintf(out, "    event.end();\n\n");
-                fprintf(out, "    event.end();\n\n");
-            }
-            argIndex++;
-        }
-
-        fprintf(out, "    return event.write(LOG_ID_STATS);\n");
-        fprintf(out, "  } else {\n");
-        fprintf(out, "    return 1;\n");
-        fprintf(out, "  }\n");
-        fprintf(out, "}\n");
-        fprintf(out, "\n");
-    }
-
-    for (auto signature_it = atoms.non_chained_signatures_to_modules.begin();
-            signature_it != atoms.non_chained_signatures_to_modules.end(); signature_it++) {
-       if (!signature_needed_for_module(signature_it->second, moduleName)) {
-           continue;
-       }
-       vector<java_type_t> signature = signature_it->first;
-       int argIndex;
-
-       fprintf(out, "int\n");
-       fprintf(out, "stats_write_non_chained(int32_t code");
-       argIndex = 1;
-       for (vector<java_type_t>::const_iterator arg = signature.begin();
-           arg != signature.end(); arg++) {
-           fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
-           argIndex++;
-       }
-       fprintf(out, ")\n");
-
-       fprintf(out, "{\n");
-
-       fprintf(out, "  int ret = 0;\n");
-       fprintf(out, "  for(int retry = 0; retry < 2; ++retry) {\n");
-       fprintf(out, "      ret =  try_stats_write_non_chained(code");
-
-       argIndex = 1;
-       for (vector<java_type_t>::const_iterator arg = signature.begin();
-           arg != signature.end(); arg++) {
-           fprintf(out, ", arg%d",   argIndex);
-           argIndex++;
-       }
-       fprintf(out, ");\n");
-       fprintf(out, "      if (ret >= 0) { break; }\n");
-
-       fprintf(out, "      {\n");
-       fprintf(out, "          std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
-       fprintf(out, "          if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
-                                "kMinRetryIntervalNs) break;\n");
-       fprintf(out, "          lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
-       fprintf(out, "      }\n");
-
-       fprintf(out, "      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
-       fprintf(out, "  }\n");
-       fprintf(out, "  if (ret < 0) {\n");
-       fprintf(out, "      note_log_drop(ret, code);\n");
-       fprintf(out, "  }\n");
-       fprintf(out, "  return ret;\n\n");
-       fprintf(out, "}\n");
-
-       fprintf(out, "\n");
-   }
-
-
-    // Print footer
-    fprintf(out, "\n");
-    write_closing_namespace(out, cppNamespace);
-
-    return 0;
-}
-
-static void write_cpp_method_header(
-        FILE* out,
-        const string& method_name,
-        const map<vector<java_type_t>, set<string>>& signatures_to_modules,
-        const AtomDecl &attributionDecl, const string& moduleName) {
-
-    for (auto signature_to_modules_it = signatures_to_modules.begin();
-            signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
-        // Skip if this signature is not needed for the module.
-        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
-            continue;
-        }
-
-        vector<java_type_t> signature = signature_to_modules_it->first;
-        fprintf(out, "int %s(int32_t code", method_name.c_str());
-        int argIndex = 1;
-        for (vector<java_type_t>::const_iterator arg = signature.begin();
-                arg != signature.end(); arg++) {
-            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
-                for (auto chainField : attributionDecl.fields) {
-                    if (chainField.javaType == JAVA_TYPE_STRING) {
-                        fprintf(out, ", const std::vector<%s>& %s",
-                            cpp_type_name(chainField.javaType), chainField.name.c_str());
-                    } else {
-                        fprintf(out, ", const %s* %s, size_t %s_length",
-                            cpp_type_name(chainField.javaType),
-                            chainField.name.c_str(), chainField.name.c_str());
-                    }
-                }
-            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
-                fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
-                             "const std::map<int, int64_t>& arg%d_2, "
-                             "const std::map<int, char const*>& arg%d_3, "
-                             "const std::map<int, float>& arg%d_4",
-                             argIndex, argIndex, argIndex, argIndex);
-            } else {
-                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
-            }
-            argIndex++;
-        }
-        fprintf(out, ");\n");
-
-    }
-}
-
-static int
-write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-        const string& moduleName, const string& cppNamespace)
-{
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "#pragma once\n");
-    fprintf(out, "\n");
-    fprintf(out, "#include <stdint.h>\n");
-    fprintf(out, "#include <vector>\n");
-    fprintf(out, "#include <map>\n");
-    fprintf(out, "#include <set>\n");
-    fprintf(out, "\n");
-
-    write_namespace(out, cppNamespace);
-    fprintf(out, "\n");
-    fprintf(out, "/*\n");
-    fprintf(out, " * API For logging statistics events.\n");
-    fprintf(out, " */\n");
-    fprintf(out, "\n");
-
-    write_native_atom_constants(out, atoms, attributionDecl, moduleName);
-
-    // Print constants for the enum values.
-    fprintf(out, "//\n");
-    fprintf(out, "// Constants for enum values\n");
-    fprintf(out, "//\n\n");
-    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-        atom != atoms.decls.end(); atom++) {
-        // Skip if the atom is not needed for the module.
-        if (!atom_needed_for_module(*atom, moduleName)) {
-            continue;
-        }
-
-        for (vector<AtomField>::const_iterator field = atom->fields.begin();
-            field != atom->fields.end(); field++) {
-            if (field->javaType == JAVA_TYPE_ENUM) {
-                fprintf(out, "// Values for %s.%s\n", atom->message.c_str(),
-                    field->name.c_str());
-                for (map<int, string>::const_iterator value = field->enumValues.begin();
-                    value != field->enumValues.end(); value++) {
-                    fprintf(out, "const int32_t %s__%s__%s = %d;\n",
-                        make_constant_name(atom->message).c_str(),
-                        make_constant_name(field->name).c_str(),
-                        make_constant_name(value->second).c_str(),
-                        value->first);
-                }
-                fprintf(out, "\n");
-            }
-        }
-    }
-
-    fprintf(out, "struct BytesField {\n");
-    fprintf(out,
-            "  BytesField(char const* array, size_t len) : arg(array), "
-            "arg_length(len) {}\n");
-    fprintf(out, "  char const* arg;\n");
-    fprintf(out, "  size_t arg_length;\n");
-    fprintf(out, "};\n");
-    fprintf(out, "\n");
-
-    // Print write methods
-    fprintf(out, "//\n");
-    fprintf(out, "// Write methods\n");
-    fprintf(out, "//\n");
-    write_cpp_method_header(out, "stats_write", atoms.signatures_to_modules, attributionDecl,
-            moduleName);
-
-    fprintf(out, "//\n");
-    fprintf(out, "// Write flattened methods\n");
-    fprintf(out, "//\n");
-    write_cpp_method_header(out, "stats_write_non_chained", atoms.non_chained_signatures_to_modules,
-        attributionDecl, moduleName);
-
-    fprintf(out, "\n");
-    write_closing_namespace(out, cppNamespace);
-
-    return 0;
-}
-
 // Hide the JNI write helpers that are not used in the new schema.
 // TODO(b/145100015): Remove this and other JNI related functionality once StatsEvent migration is
 // complete.
@@ -999,7 +510,9 @@
     fprintf(stderr, "                                    required for java with module\n");
     fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
     fprintf(stderr, "                       Optional for Java with module.\n");
-    fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");}
+    fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");
+    fprintf(stderr, "  --supportQ           Include support for Android Q.\n");
+}
 
 /**
  * Do the argument parsing and execute the tasks.
@@ -1020,6 +533,7 @@
     string atomsInfoCppHeaderImport = DEFAULT_ATOMS_INFO_CPP_HEADER_IMPORT;
     string javaPackage = DEFAULT_JAVA_PACKAGE;
     string javaClass = DEFAULT_JAVA_CLASS;
+    bool supportQ = false;
 
     int index = 1;
     while (index < argc) {
@@ -1110,6 +624,8 @@
                 return 1;
             }
             atomsInfoCppHeaderImport = argv[index];
+        } else if (0 == strcmp("--supportQ", argv[index])) {
+            supportQ = true;
         }
 
         index++;
@@ -1125,6 +641,12 @@
         return 1;
     }
 
+    if (DEFAULT_MODULE_NAME == moduleName && supportQ) {
+        // Support for Q schema is not needed for default module.
+        fprintf(stderr, "%s cannot support Q schema\n", moduleName.c_str());
+        return 1;
+    }
+
     // Collate the parameters
     Atoms atoms;
     int errorCount = collate_atoms(Atom::descriptor(), &atoms);
@@ -1179,7 +701,7 @@
             return 1;
         }
         errorCount = android::stats_log_api_gen::write_stats_log_cpp(
-            out, atoms, attributionDecl, moduleName, cppNamespace, cppHeaderImport);
+            out, atoms, attributionDecl, moduleName, cppNamespace, cppHeaderImport, supportQ);
         fclose(out);
     }
 
@@ -1227,7 +749,7 @@
             javaPackage = "android.util";
         }
         errorCount = android::stats_log_api_gen::write_stats_log_java(
-                out, atoms, attributionDecl, moduleName, javaClass, javaPackage);
+                out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ);
 #endif
 
         fclose(out);
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
new file mode 100644
index 0000000..c7a34fe
--- /dev/null
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 "native_writer.h"
+#include "native_writer_q.h"
+#include "utils.h"
+
+namespace android {
+namespace stats_log_api_gen {
+
+#if !defined(STATS_SCHEMA_LEGACY)
+static void write_native_key_value_pairs_for_type(FILE* out, const int argIndex,
+        const int typeIndex, const string& type, const string& valueFieldName) {
+    fprintf(out, "    for (const auto& it : arg%d_%d) {\n", argIndex, typeIndex);
+    fprintf(out, "        pairs.push_back("
+            "{ .key = it.first, .valueType = %s, .%s = it.second });\n",
+            type.c_str(), valueFieldName.c_str());
+    fprintf(out, "    }\n");
+
+}
+
+static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName, const bool supportQ) {
+    fprintf(out, "\n");
+    for (auto signature_to_modules_it = atoms.signatures_to_modules.begin();
+        signature_to_modules_it != atoms.signatures_to_modules.end(); signature_to_modules_it++) {
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
+        vector<java_type_t> signature = signature_to_modules_it->first;
+
+        write_native_method_signature(out, "int stats_write", signature,
+                attributionDecl, " {");
+
+        int argIndex = 1;
+        if (supportQ) {
+            fprintf(out, "    StatsEventCompat event;\n");
+            fprintf(out, "    event.setAtomId(code);\n");
+            for (vector<java_type_t>::const_iterator arg = signature.begin();
+                    arg != signature.end(); arg++) {
+                switch (*arg) {
+                    case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                        const char* uidName = attributionDecl.fields.front().name.c_str();
+                        const char* tagName = attributionDecl.fields.back().name.c_str();
+                        fprintf(out, "    event.writeAttributionChain(%s, %s_length, %s);\n",
+                                uidName, uidName, tagName);
+                        break;
+                    }
+                    case JAVA_TYPE_KEY_VALUE_PAIR:
+                        fprintf(out, "    event.writeKeyValuePairs("
+                                "arg%d_1, arg%d_2, arg%d_3, arg%d_4);\n",
+                                argIndex, argIndex, argIndex, argIndex);
+                        break;
+                    case JAVA_TYPE_BYTE_ARRAY:
+                        fprintf(out, "    event.writeByteArray(arg%d.arg, arg%d.arg_length);\n",
+                                argIndex, argIndex);
+                        break;
+                    case JAVA_TYPE_BOOLEAN:
+                        fprintf(out, "    event.writeBool(arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_INT: // Fall through.
+                    case JAVA_TYPE_ENUM:
+                        fprintf(out, "    event.writeInt32(arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_FLOAT:
+                        fprintf(out, "    event.writeFloat(arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_LONG:
+                        fprintf(out, "    event.writeInt64(arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_STRING:
+                        fprintf(out, "    event.writeString(arg%d);\n", argIndex);
+                        break;
+                    default:
+                        // Unsupported types: OBJECT, DOUBLE.
+                        fprintf(stderr, "Encountered unsupported type.");
+                        return 1;
+                }
+                argIndex++;
+            }
+            fprintf(out, "    return event.writeToSocket();\n");
+        } else {
+            fprintf(out, "    struct stats_event* event = stats_event_obtain();\n");
+            fprintf(out, "    stats_event_set_atom_id(event, code);\n");
+            for (vector<java_type_t>::const_iterator arg = signature.begin();
+                    arg != signature.end(); arg++) {
+                switch (*arg) {
+                    case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+                        const char* uidName = attributionDecl.fields.front().name.c_str();
+                        const char* tagName = attributionDecl.fields.back().name.c_str();
+                        fprintf(out,
+                                "    stats_event_write_attribution_chain(event, "
+                                "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
+                                "static_cast<uint8_t>(%s_length));\n",
+                                uidName, tagName, uidName);
+                        break;
+                    }
+                    case JAVA_TYPE_KEY_VALUE_PAIR:
+                        fprintf(out, "    std::vector<key_value_pair> pairs;\n");
+                        write_native_key_value_pairs_for_type(
+                                out, argIndex, 1, "INT32_TYPE", "int32Value");
+                        write_native_key_value_pairs_for_type(
+                                out, argIndex, 2, "INT64_TYPE", "int64Value");
+                        write_native_key_value_pairs_for_type(
+                                out, argIndex, 3, "STRING_TYPE", "stringValue");
+                        write_native_key_value_pairs_for_type(
+                                out, argIndex, 4, "FLOAT_TYPE", "floatValue");
+                        fprintf(out,
+                                "    stats_event_write_key_value_pairs(event, pairs.data(), "
+                                "static_cast<uint8_t>(pairs.size()));\n");
+                        break;
+                    case JAVA_TYPE_BYTE_ARRAY:
+                        fprintf(out,
+                                "    stats_event_write_byte_array(event, "
+                                "reinterpret_cast<const uint8_t*>(arg%d.arg), arg%d.arg_length);\n",
+                                argIndex, argIndex);
+                        break;
+                    case JAVA_TYPE_BOOLEAN:
+                        fprintf(out, "    stats_event_write_bool(event, arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_INT: // Fall through.
+                    case JAVA_TYPE_ENUM:
+                        fprintf(out, "    stats_event_write_int32(event, arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_FLOAT:
+                        fprintf(out, "    stats_event_write_float(event, arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_LONG:
+                        fprintf(out, "    stats_event_write_int64(event, arg%d);\n", argIndex);
+                        break;
+                    case JAVA_TYPE_STRING:
+                        fprintf(out, "    stats_event_write_string8(event, arg%d);\n", argIndex);
+                        break;
+                    default:
+                        // Unsupported types: OBJECT, DOUBLE.
+                        fprintf(stderr, "Encountered unsupported type.");
+                        return 1;
+                }
+                argIndex++;
+            }
+            fprintf(out, "    const int ret = stats_event_write(event);\n");
+            fprintf(out, "    stats_event_release(event);\n");
+            fprintf(out, "    return ret;\n");
+        }
+        fprintf(out, "}\n\n");
+    }
+    return 0;
+}
+
+static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName) {
+    fprintf(out, "\n");
+    for (auto signature_it = atoms.non_chained_signatures_to_modules.begin();
+            signature_it != atoms.non_chained_signatures_to_modules.end(); signature_it++) {
+        if (!signature_needed_for_module(signature_it->second, moduleName)) {
+            continue;
+        }
+        vector<java_type_t> signature = signature_it->first;
+
+        write_native_method_signature(out, "int stats_write_non_chained", signature,
+                attributionDecl, " {");
+
+        vector<java_type_t> newSignature;
+
+        // First two args form the attribution node so size goes down by 1.
+        newSignature.reserve(signature.size() - 1);
+
+        // First arg is Attribution Chain.
+        newSignature.push_back(JAVA_TYPE_ATTRIBUTION_CHAIN);
+
+        // Followed by the originial signature except the first 2 args.
+        newSignature.insert(newSignature.end(), signature.begin() + 2, signature.end());
+
+        const char* uidName = attributionDecl.fields.front().name.c_str();
+        const char* tagName = attributionDecl.fields.back().name.c_str();
+        fprintf(out, "    const int32_t* %s = &arg1;\n", uidName);
+        fprintf(out, "    const size_t %s_length = 1;\n", uidName);
+        fprintf(out, "    const std::vector<char const*> %s(1, arg2);\n", tagName);
+        fprintf(out, "    return ");
+        write_native_method_call(out, "stats_write", newSignature, attributionDecl, 2);
+
+        fprintf(out, "}\n\n");
+    }
+
+}
+#endif
+
+static void write_native_method_header(
+        FILE* out,
+        const string& methodName,
+        const map<vector<java_type_t>, set<string>>& signatures_to_modules,
+        const AtomDecl &attributionDecl, const string& moduleName) {
+
+    for (auto signature_to_modules_it = signatures_to_modules.begin();
+            signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
+        // Skip if this signature is not needed for the module.
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
+
+        vector<java_type_t> signature = signature_to_modules_it->first;
+        write_native_method_signature(out, methodName, signature, attributionDecl, ";");
+    }
+}
+
+int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
+                        const string& moduleName, const string& cppNamespace,
+                        const string& importHeader, const bool supportQ) {
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+
+    fprintf(out, "#include <%s>\n", importHeader.c_str());
+#if defined(STATS_SCHEMA_LEGACY)
+    (void)supportQ; // Workaround for unused parameter error.
+    write_native_cpp_includes_q(out);
+#else
+    if (supportQ) {
+        fprintf(out, "#include <StatsEventCompat.h>\n");
+    } else {
+        fprintf(out, "#include <stats_event.h>\n");
+    }
+#endif
+
+    fprintf(out, "\n");
+    write_namespace(out, cppNamespace);
+
+#if defined(STATS_SCHEMA_LEGACY)
+    write_native_stats_log_cpp_globals_q(out);
+    write_native_get_timestamp_ns_q(out);
+    write_native_try_stats_write_methods_q(out, atoms, attributionDecl, moduleName);
+    write_native_stats_write_methods_q(out, "int stats_write", atoms, attributionDecl, moduleName,
+            "try_stats_write");
+    write_native_try_stats_write_non_chained_methods_q(out, atoms, attributionDecl, moduleName);
+    write_native_stats_write_non_chained_methods_q(out, "int stats_write_non_chained", atoms,
+            attributionDecl, moduleName, "try_stats_write_non_chained");
+#else
+    write_native_stats_write_methods(out, atoms, attributionDecl, moduleName, supportQ);
+    write_native_stats_write_non_chained_methods(out, atoms, attributionDecl, moduleName);
+#endif
+
+    // Print footer
+    fprintf(out, "\n");
+    write_closing_namespace(out, cppNamespace);
+
+    return 0;
+}
+
+int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
+        const string& moduleName, const string& cppNamespace) {
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+    fprintf(out, "#pragma once\n");
+    fprintf(out, "\n");
+    fprintf(out, "#include <stdint.h>\n");
+    fprintf(out, "#include <vector>\n");
+    fprintf(out, "#include <map>\n");
+    fprintf(out, "#include <set>\n");
+    fprintf(out, "\n");
+
+    write_namespace(out, cppNamespace);
+    fprintf(out, "\n");
+    fprintf(out, "/*\n");
+    fprintf(out, " * API For logging statistics events.\n");
+    fprintf(out, " */\n");
+    fprintf(out, "\n");
+
+    write_native_atom_constants(out, atoms, attributionDecl, moduleName);
+
+    // Print constants for the enum values.
+    fprintf(out, "//\n");
+    fprintf(out, "// Constants for enum values\n");
+    fprintf(out, "//\n\n");
+    for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
+        atom != atoms.decls.end(); atom++) {
+        // Skip if the atom is not needed for the module.
+        if (!atom_needed_for_module(*atom, moduleName)) {
+            continue;
+        }
+
+        for (vector<AtomField>::const_iterator field = atom->fields.begin();
+            field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ENUM) {
+                fprintf(out, "// Values for %s.%s\n", atom->message.c_str(),
+                    field->name.c_str());
+                for (map<int, string>::const_iterator value = field->enumValues.begin();
+                    value != field->enumValues.end(); value++) {
+                    fprintf(out, "const int32_t %s__%s__%s = %d;\n",
+                        make_constant_name(atom->message).c_str(),
+                        make_constant_name(field->name).c_str(),
+                        make_constant_name(value->second).c_str(),
+                        value->first);
+                }
+                fprintf(out, "\n");
+            }
+        }
+    }
+
+    fprintf(out, "struct BytesField {\n");
+    fprintf(out,
+            "  BytesField(char const* array, size_t len) : arg(array), "
+            "arg_length(len) {}\n");
+    fprintf(out, "  char const* arg;\n");
+    fprintf(out, "  size_t arg_length;\n");
+    fprintf(out, "};\n");
+    fprintf(out, "\n");
+
+    // Print write methods
+    fprintf(out, "//\n");
+    fprintf(out, "// Write methods\n");
+    fprintf(out, "//\n");
+    write_native_method_header(out, "int stats_write", atoms.signatures_to_modules, attributionDecl,
+            moduleName);
+
+    fprintf(out, "//\n");
+    fprintf(out, "// Write flattened methods\n");
+    fprintf(out, "//\n");
+    write_native_method_header(out, "int stats_write_non_chained",
+            atoms.non_chained_signatures_to_modules, attributionDecl, moduleName);
+
+    fprintf(out, "\n");
+    write_closing_namespace(out, cppNamespace);
+
+    return 0;
+}
+
+}  // namespace stats_log_api_gen
+}  // namespace android
diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h
new file mode 100644
index 0000000..aafa96e
--- /dev/null
+++ b/tools/stats_log_api_gen/native_writer.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "Collation.h"
+
+#include <stdio.h>
+#include <string.h>
+
+namespace android {
+namespace stats_log_api_gen {
+
+using namespace std;
+
+int write_stats_log_cpp(FILE *out, const Atoms &atoms, const AtomDecl &attributionDecl,
+        const string& moduleName, const string& cppNamespace, const string& importHeader,
+        const bool supportQ);
+
+int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
+        const string& moduleName, const string& cppNamespace);
+
+}  // namespace stats_log_api_gen
+}  // namespace android
diff --git a/tools/stats_log_api_gen/native_writer_q.cpp b/tools/stats_log_api_gen/native_writer_q.cpp
new file mode 100644
index 0000000..299873d
--- /dev/null
+++ b/tools/stats_log_api_gen/native_writer_q.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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 "native_writer_q.h"
+#include "utils.h"
+
+namespace android {
+namespace stats_log_api_gen {
+
+static void write_native_stats_write_body_q(FILE* out, const vector<java_type_t>& signature,
+        const AtomDecl& attributionDecl, const string& indent, const string& tryMethodName) {
+    fprintf(out, "%sint ret = 0;\n", indent.c_str());
+
+    fprintf(out, "%sfor(int retry = 0; retry < 2; ++retry) {\n", indent.c_str());
+    fprintf(out, "%s    ret = ", indent.c_str());
+    write_native_method_call(out, tryMethodName, signature, attributionDecl);
+    fprintf(out, "%s    if (ret >= 0) { break; }\n", indent.c_str());
+
+    fprintf(out, "%s    {\n", indent.c_str());
+    fprintf(out, "%s        std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n", indent.c_str());
+    fprintf(out, "%s        if ((get_elapsed_realtime_ns() - lastRetryTimestampNs) <= "
+                            "kMinRetryIntervalNs) break;\n", indent.c_str());
+    fprintf(out, "%s        lastRetryTimestampNs = get_elapsed_realtime_ns();\n",
+            indent.c_str());
+    fprintf(out, "%s    }\n", indent.c_str());
+    fprintf(out, "%s    std::this_thread::sleep_for(std::chrono::milliseconds(10));\n",
+            indent.c_str());
+    fprintf(out, "%s}\n", indent.c_str());
+    fprintf(out, "%sif (ret < 0) {\n", indent.c_str());
+    fprintf(out, "%s    note_log_drop(ret, code);\n", indent.c_str());
+    fprintf(out, "%s}\n", indent.c_str());
+    fprintf(out, "%sreturn ret;\n", indent.c_str());
+}
+
+void write_native_cpp_includes_q(FILE* out) {
+    fprintf(out, "#include <mutex>\n");
+    fprintf(out, "#include <chrono>\n");
+    fprintf(out, "#include <thread>\n");
+    fprintf(out, "#ifdef __ANDROID__\n");
+    fprintf(out, "#include <cutils/properties.h>\n");
+    fprintf(out, "#endif\n");
+    fprintf(out, "#include <stats_event_list.h>\n");
+    fprintf(out, "#include <log/log.h>\n");
+    fprintf(out, "#include <time.h>\n");
+}
+
+void write_native_get_timestamp_ns_q(FILE* out) {
+    fprintf(out, "\n");
+    fprintf(out, "static int64_t get_elapsed_realtime_ns() {\n");
+    fprintf(out, "    struct timespec t;\n");
+    fprintf(out, "    t.tv_sec = t.tv_nsec = 0;\n");
+    fprintf(out, "    clock_gettime(CLOCK_BOOTTIME, &t);\n");
+    fprintf(out, "    return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;\n");
+    fprintf(out, "}\n");
+}
+
+void write_native_stats_log_cpp_globals_q(FILE* out) {
+    fprintf(out, "// the single event tag id for all stats logs\n");
+    fprintf(out, "const static int kStatsEventTag = 1937006964;\n");
+    fprintf(out, "#ifdef __ANDROID__\n");
+    fprintf(out,
+            "const static bool kStatsdEnabled = property_get_bool(\"ro.statsd.enable\", true);\n");
+    fprintf(out, "#else\n");
+    fprintf(out, "const static bool kStatsdEnabled = false;\n");
+    fprintf(out, "#endif\n");
+
+    fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
+    fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
+    fprintf(out, "static std::mutex mLogdRetryMutex;\n");
+}
+
+void write_native_try_stats_write_methods_q(FILE* out, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName) {
+    fprintf(out, "\n");
+    for (auto signature_to_modules_it = atoms.signatures_to_modules.begin();
+        signature_to_modules_it != atoms.signatures_to_modules.end(); signature_to_modules_it++) {
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
+        vector<java_type_t> signature = signature_to_modules_it->first;
+
+        write_native_method_signature(out, "static int try_stats_write", signature,
+                attributionDecl, " {");
+
+        int argIndex = 1;
+        fprintf(out, "  if (kStatsdEnabled) {\n");
+        fprintf(out, "    stats_event_list event(kStatsEventTag);\n");
+        fprintf(out, "    event << get_elapsed_realtime_ns();\n\n");
+        fprintf(out, "    event << code;\n\n");
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+            arg != signature.end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (const auto &chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    if (%s_length != %s.size()) {\n",
+                            attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
+                        fprintf(out, "        return -EINVAL;\n");
+                        fprintf(out, "    }\n");
+                    }
+                }
+                fprintf(out, "\n    event.begin();\n");
+                fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                    attributionDecl.fields.front().name.c_str());
+                fprintf(out, "        event.begin();\n");
+                for (const auto &chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "        if (%s[i] != NULL) {\n", chainField.name.c_str());
+                        fprintf(out, "           event << %s[i];\n", chainField.name.c_str());
+                        fprintf(out, "        } else {\n");
+                        fprintf(out, "           event << \"\";\n");
+                        fprintf(out, "        }\n");
+                    } else {
+                        fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
+                    }
+                }
+                fprintf(out, "        event.end();\n");
+                fprintf(out, "    }\n");
+                fprintf(out, "    event.end();\n\n");
+            } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+                    fprintf(out, "    event.begin();\n\n");
+                    fprintf(out, "    for (const auto& it : arg%d_1) {\n", argIndex);
+                    fprintf(out, "         event.begin();\n");
+                    fprintf(out, "         event << it.first;\n");
+                    fprintf(out, "         event << it.second;\n");
+                    fprintf(out, "         event.end();\n");
+                    fprintf(out, "    }\n");
+
+                    fprintf(out, "    for (const auto& it : arg%d_2) {\n", argIndex);
+                    fprintf(out, "         event.begin();\n");
+                    fprintf(out, "         event << it.first;\n");
+                    fprintf(out, "         event << it.second;\n");
+                    fprintf(out, "         event.end();\n");
+                    fprintf(out, "    }\n");
+
+                    fprintf(out, "    for (const auto& it : arg%d_3) {\n", argIndex);
+                    fprintf(out, "         event.begin();\n");
+                    fprintf(out, "         event << it.first;\n");
+                    fprintf(out, "         event << it.second;\n");
+                    fprintf(out, "         event.end();\n");
+                    fprintf(out, "    }\n");
+
+                    fprintf(out, "    for (const auto& it : arg%d_4) {\n", argIndex);
+                    fprintf(out, "         event.begin();\n");
+                    fprintf(out, "         event << it.first;\n");
+                    fprintf(out, "         event << it.second;\n");
+                    fprintf(out, "         event.end();\n");
+                    fprintf(out, "    }\n");
+
+                    fprintf(out, "    event.end();\n\n");
+            } else if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out,
+                        "    event.AppendCharArray(arg%d.arg, "
+                        "arg%d.arg_length);\n",
+                        argIndex, argIndex);
+            } else {
+                if (*arg == JAVA_TYPE_STRING) {
+                    fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
+                    fprintf(out, "        arg%d = \"\";\n", argIndex);
+                    fprintf(out, "    }\n");
+                }
+                fprintf(out, "    event << arg%d;\n", argIndex);
+            }
+            argIndex++;
+        }
+
+        fprintf(out, "    return event.write(LOG_ID_STATS);\n");
+        fprintf(out, "  } else {\n");
+        fprintf(out, "    return 1;\n");
+        fprintf(out, "  }\n");
+        fprintf(out, "}\n");
+        fprintf(out, "\n");
+    }
+
+}
+
+void write_native_stats_write_methods_q(FILE* out, const string& methodName, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName, const string& tryMethodName) {
+    for (auto signature_to_modules_it = atoms.signatures_to_modules.begin();
+        signature_to_modules_it != atoms.signatures_to_modules.end();
+        signature_to_modules_it++) {
+        if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
+            continue;
+        }
+        vector<java_type_t> signature = signature_to_modules_it->first;
+
+        write_native_method_signature(out, methodName, signature, attributionDecl, " {");
+
+        write_native_stats_write_body_q(out, signature, attributionDecl, "    ", tryMethodName);
+        fprintf(out, "}\n\n");
+    }
+}
+
+void write_native_stats_write_non_chained_methods_q(FILE* out, const string& methodName,
+        const Atoms& atoms, const AtomDecl& attributionDecl, const string& moduleName,
+        const string& tryMethodName) {
+    for (auto signature_it = atoms.non_chained_signatures_to_modules.begin();
+            signature_it != atoms.non_chained_signatures_to_modules.end(); signature_it++) {
+        if (!signature_needed_for_module(signature_it->second, moduleName)) {
+            continue;
+        }
+        vector<java_type_t> signature = signature_it->first;
+
+        write_native_method_signature(out, methodName, signature, attributionDecl, " {");
+
+        write_native_stats_write_body_q(out, signature, attributionDecl, "    ", tryMethodName);
+        fprintf(out, "}\n\n");
+    }
+}
+
+void write_native_try_stats_write_non_chained_methods_q(FILE* out, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName) {
+    for (auto signature_it = atoms.non_chained_signatures_to_modules.begin();
+            signature_it != atoms.non_chained_signatures_to_modules.end(); signature_it++) {
+        if (!signature_needed_for_module(signature_it->second, moduleName)) {
+            continue;
+        }
+        vector<java_type_t> signature = signature_it->first;
+
+        write_native_method_signature(out, "static int try_stats_write_non_chained", signature,
+                attributionDecl, " {");
+
+        int argIndex = 1;
+        fprintf(out, "  if (kStatsdEnabled) {\n");
+        fprintf(out, "    stats_event_list event(kStatsEventTag);\n");
+        fprintf(out, "    event << get_elapsed_realtime_ns();\n\n");
+        fprintf(out, "    event << code;\n\n");
+        for (vector<java_type_t>::const_iterator arg = signature.begin();
+            arg != signature.end(); arg++) {
+            if (argIndex == 1) {
+                fprintf(out, "    event.begin();\n\n");
+                fprintf(out, "    event.begin();\n");
+            }
+            if (*arg == JAVA_TYPE_STRING) {
+                fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
+                fprintf(out, "        arg%d = \"\";\n", argIndex);
+                fprintf(out, "    }\n");
+            }
+            if (*arg == JAVA_TYPE_BYTE_ARRAY) {
+                fprintf(out,
+                        "    event.AppendCharArray(arg%d.arg, "
+                        "arg%d.arg_length);\n",
+                        argIndex, argIndex);
+            } else {
+                fprintf(out, "    event << arg%d;\n", argIndex);
+            }
+            if (argIndex == 2) {
+                fprintf(out, "    event.end();\n\n");
+                fprintf(out, "    event.end();\n\n");
+            }
+            argIndex++;
+        }
+
+        fprintf(out, "    return event.write(LOG_ID_STATS);\n");
+        fprintf(out, "  } else {\n");
+        fprintf(out, "    return 1;\n");
+        fprintf(out, "  }\n");
+        fprintf(out, "}\n");
+        fprintf(out, "\n");
+    }
+}
+
+}  // namespace stats_log_api_gen
+}  // namespace android
diff --git a/tools/stats_log_api_gen/native_writer_q.h b/tools/stats_log_api_gen/native_writer_q.h
new file mode 100644
index 0000000..a2ab1ae
--- /dev/null
+++ b/tools/stats_log_api_gen/native_writer_q.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "Collation.h"
+
+#include <stdio.h>
+#include <string.h>
+
+namespace android {
+namespace stats_log_api_gen {
+
+using namespace std;
+
+void write_native_cpp_includes_q(FILE* out);
+
+void write_native_stats_log_cpp_globals_q(FILE* out);
+
+void write_native_try_stats_write_methods_q(FILE* out, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName);
+
+void write_native_stats_write_methods_q(FILE* out, const string& methodName, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName, const string& tryMethodName);
+
+void write_native_try_stats_write_non_chained_methods_q(FILE* out, const Atoms& atoms,
+        const AtomDecl& attributionDecl, const string& moduleName);
+
+void write_native_stats_write_non_chained_methods_q(FILE* out, const string& methodName,
+        const Atoms& atoms, const AtomDecl& attributionDecl, const string& moduleName,
+        const string& tryMethodName);
+
+void write_native_get_timestamp_ns_q(FILE* out);
+
+}  // namespace stats_log_api_gen
+}  // namespace android
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index c3e70382..c9a4763 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -229,8 +229,8 @@
 
 message ModuleAtoms {
     oneof event {
-        ModuleOneAtom module_one_atom = 1 [(android.os.statsd.log_from_module) = "module1"];
-        ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.log_from_module) = "module2"];
+        ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"];
+        ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"];
         NoModuleAtom no_module_atom = 3;
     }
-}
\ No newline at end of file
+}
diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp
index d6cfe95..c65d190 100644
--- a/tools/stats_log_api_gen/utils.cpp
+++ b/tools/stats_log_api_gen/utils.cpp
@@ -204,6 +204,65 @@
     fprintf(out, "\n");
 }
 
+void write_native_method_signature(FILE* out, const string& methodName,
+        const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
+        const string& closer) {
+    fprintf(out, "%s(int32_t code", methodName.c_str());
+    int argIndex = 1;
+    for (vector<java_type_t>::const_iterator arg = signature.begin();
+        arg != signature.end(); arg++) {
+        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+            for (auto chainField : attributionDecl.fields) {
+                if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", const std::vector<%s>& %s",
+                             cpp_type_name(chainField.javaType),
+                             chainField.name.c_str());
+                } else {
+                        fprintf(out, ", const %s* %s, size_t %s_length",
+                             cpp_type_name(chainField.javaType),
+                             chainField.name.c_str(), chainField.name.c_str());
+                }
+            }
+        } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+            fprintf(out, ", const std::map<int, int32_t>& arg%d_1, "
+                         "const std::map<int, int64_t>& arg%d_2, "
+                         "const std::map<int, char const*>& arg%d_3, "
+                         "const std::map<int, float>& arg%d_4",
+                         argIndex, argIndex, argIndex, argIndex);
+        } else {
+            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+        }
+        argIndex++;
+    }
+    fprintf(out, ")%s\n", closer.c_str());
+}
+
+void write_native_method_call(FILE* out, const string& methodName,
+        const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex) {
+    fprintf(out, "%s(code", methodName.c_str());
+    for (vector<java_type_t>::const_iterator arg = signature.begin();
+       arg != signature.end(); arg++) {
+       if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+           for (auto chainField : attributionDecl.fields) {
+               if (chainField.javaType == JAVA_TYPE_STRING) {
+                       fprintf(out, ", %s",
+                            chainField.name.c_str());
+               } else {
+                       fprintf(out, ",  %s,  %s_length",
+                            chainField.name.c_str(), chainField.name.c_str());
+               }
+           }
+       } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+           fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex,
+                   argIndex, argIndex, argIndex);
+       } else {
+           fprintf(out, ", arg%d", argIndex);
+       }
+       argIndex++;
+    }
+    fprintf(out, ");\n");
+}
+
 // Java
 void write_java_atom_codes(FILE* out, const Atoms& atoms, const string& moduleName) {
     fprintf(out, "    // Constants for atom codes.\n");
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
index a89387f..50737a6 100644
--- a/tools/stats_log_api_gen/utils.h
+++ b/tools/stats_log_api_gen/utils.h
@@ -56,8 +56,14 @@
 void write_closing_namespace(FILE* out, const string& cppNamespaces);
 
 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
-        const string& moduleName
-);
+        const string& moduleName);
+
+void write_native_method_signature(FILE* out, const string& methodName,
+        const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
+        const string& closer);
+
+void write_native_method_call(FILE* out, const string& methodName,
+        const vector<java_type_t>& signature, const AtomDecl& attributionDecl, int argIndex = 1);
 
 // Common Java helpers.
 void write_java_atom_codes(FILE* out, const Atoms& atoms, const string& moduleName);
@@ -69,14 +75,12 @@
 
 int write_java_non_chained_methods(FILE* out, const map<vector<java_type_t>,
         set<string>>& signatures_to_modules,
-        const string& moduleName
-);
+        const string& moduleName);
 
 int write_java_work_source_methods(
         FILE* out,
         const map<vector<java_type_t>, set<string>>& signatures_to_modules,
-        const string& moduleName
-);
+        const string& moduleName);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/wifi/Android.bp b/wifi/Android.bp
index fb1f866..634f674 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -42,11 +42,23 @@
     srcs: ["java/android/net/wifi/WifiAnnotations.java"],
 }
 
+// list of tests that are allowed to access @hide APIs from framework-wifi
+test_access_hidden_api_whitelist = [
+    "//frameworks/base/wifi/tests",
+    "//frameworks/opt/net/wifi/tests/wifitests:__subpackages__",
+
+    "//frameworks/opt/net/wifi/libs/WifiTrackerLib/tests",
+    "//external/robolectric-shadows:__subpackages__",
+]
+
 java_library {
     name: "framework-wifi",
-    sdk_version: "core_platform", // TODO(b/140299412) should be core_current
+    // TODO(b/140299412) should be core_current once we build against framework-system-stubs
+    sdk_version: "core_platform",
     libs: [
-        "framework-minus-apex", // TODO(b/140299412) should be framework-system-stubs
+        // TODO(b/140299412) should be framework-system-stubs once we fix all @hide dependencies
+        "framework-minus-apex",
+        "unsupportedappusage",
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
@@ -54,7 +66,12 @@
     installable: true,
     optimize: {
         enabled: false
-    }
+    },
+    visibility: [
+        "//frameworks/base", // TODO(b/140299412) remove once all dependencies are fixed
+        "//frameworks/opt/net/wifi/service:__subpackages__",
+    ] + test_access_hidden_api_whitelist,
+    plugins: ["java_api_finder"],
 }
 
 droidstubs {
@@ -84,3 +101,16 @@
     installable: false,
 }
 
+// defaults for tests that need to build against framework-wifi's @hide APIs
+java_defaults {
+    name: "framework-wifi-test-defaults",
+    sdk_version: "core_platform", // tests can use @CorePlatformApi's
+    libs: [
+        "framework-wifi",
+        "framework-minus-apex",
+
+        // if sdk_version="" this gets automatically included, but here we need to add manually.
+        "framework-res",
+    ],
+    visibility: test_access_hidden_api_whitelist,
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 83a1800..c6aca07 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -82,16 +82,19 @@
      * @hide
      * No security protocol.
      */
+    @SystemApi
     public static final int PROTOCOL_NONE = 0;
     /**
      * @hide
      * Security protocol type: WPA version 1.
      */
+    @SystemApi
     public static final int PROTOCOL_WPA = 1;
     /**
      * @hide
      * Security protocol type: RSN, for WPA version 2, and version 3.
      */
+    @SystemApi
     public static final int PROTOCOL_RSN = 2;
     /**
      * @hide
@@ -99,79 +102,94 @@
      * OSU Server-only authenticated layer 2 Encryption Network.
      * Used for Hotspot 2.0.
      */
+    @SystemApi
     public static final int PROTOCOL_OSEN = 3;
 
     /**
      * @hide
      * Security protocol type: WAPI.
      */
+    @SystemApi
     public static final int PROTOCOL_WAPI = 4;
 
     /**
      * @hide
      * No security key management scheme.
      */
+    @SystemApi
     public static final int KEY_MGMT_NONE = 0;
     /**
      * @hide
      * Security key management scheme: PSK.
      */
+    @SystemApi
     public static final int KEY_MGMT_PSK = 1;
     /**
      * @hide
      * Security key management scheme: EAP.
      */
+    @SystemApi
     public static final int KEY_MGMT_EAP = 2;
     /**
      * @hide
      * Security key management scheme: FT_PSK.
      */
+    @SystemApi
     public static final int KEY_MGMT_FT_PSK = 3;
     /**
      * @hide
      * Security key management scheme: FT_EAP.
      */
+    @SystemApi
     public static final int KEY_MGMT_FT_EAP = 4;
     /**
      * @hide
      * Security key management scheme: PSK_SHA256
      */
+    @SystemApi
     public static final int KEY_MGMT_PSK_SHA256 = 5;
     /**
      * @hide
      * Security key management scheme: EAP_SHA256.
      */
+    @SystemApi
     public static final int KEY_MGMT_EAP_SHA256 = 6;
     /**
      * @hide
      * Security key management scheme: OSEN.
      * Used for Hotspot 2.0.
      */
+    @SystemApi
     public static final int KEY_MGMT_OSEN = 7;
      /**
      * @hide
      * Security key management scheme: SAE.
      */
+    @SystemApi
     public static final int KEY_MGMT_SAE = 8;
     /**
      * @hide
      * Security key management scheme: OWE.
      */
+    @SystemApi
     public static final int KEY_MGMT_OWE = 9;
     /**
      * @hide
      * Security key management scheme: SUITE_B_192.
      */
+    @SystemApi
     public static final int KEY_MGMT_EAP_SUITE_B_192 = 10;
     /**
      * @hide
      * Security key management scheme: FT_SAE.
      */
+    @SystemApi
     public static final int KEY_MGMT_FT_SAE = 11;
     /**
      * @hide
      * Security key management scheme: OWE in transition mode.
      */
+    @SystemApi
     public static final int KEY_MGMT_OWE_TRANSITION = 12;
     /**
      * @hide
@@ -185,35 +203,42 @@
      */
     @SystemApi
     public static final int KEY_MGMT_WAPI_CERT = 14;
+
     /**
      * @hide
      * No cipher suite.
      */
+    @SystemApi
     public static final int CIPHER_NONE = 0;
     /**
      * @hide
      * No group addressed, only used for group data cipher.
      */
+    @SystemApi
     public static final int CIPHER_NO_GROUP_ADDRESSED = 1;
     /**
      * @hide
      * Cipher suite: TKIP
      */
+    @SystemApi
     public static final int CIPHER_TKIP = 2;
     /**
      * @hide
      * Cipher suite: CCMP
      */
+    @SystemApi
     public static final int CIPHER_CCMP = 3;
     /**
      * @hide
      * Cipher suite: GCMP
      */
+    @SystemApi
     public static final int CIPHER_GCMP_256 = 4;
     /**
      * @hide
      * Cipher suite: SMS4
      */
+    @SystemApi
     public static final int CIPHER_SMS4 = 5;
 
     /**
diff --git a/wifi/java/android/net/wifi/SoftApCapability.java b/wifi/java/android/net/wifi/SoftApCapability.java
index c4474e2..2bbe7d2 100644
--- a/wifi/java/android/net/wifi/SoftApCapability.java
+++ b/wifi/java/android/net/wifi/SoftApCapability.java
@@ -61,11 +61,20 @@
      */
     public static final int SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT = 1 << 1;
 
+
+    /**
+     * Support for WPA3 Simultaneous Authentication of Equals (WPA3-SAE).
+     *
+     * flag when {@link config_wifi_softap_sae_supported)} is true.
+     */
+    public static final int SOFTAP_FEATURE_WPA3_SAE = 1 << 2;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "SOFTAP_FEATURE_" }, value = {
             SOFTAP_FEATURE_ACS_OFFLOAD,
             SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT,
+            SOFTAP_FEATURE_WPA3_SAE,
     })
     public @interface HotspotFeatures {}
 
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 05e245b..65e9b79 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -25,6 +25,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -55,6 +56,11 @@
 @SystemApi
 public final class SoftApConfiguration implements Parcelable {
 
+    @VisibleForTesting
+    static final int PSK_MIN_LEN = 8;
+    @VisibleForTesting
+    static final int PSK_MAX_LEN = 63;
+
     /**
      * 2GHz band.
      * @hide
@@ -142,9 +148,10 @@
     private final @Nullable MacAddress mBssid;
 
     /**
-     * Pre-shared key for WPA2-PSK encryption (non-null enables WPA2-PSK).
+     * Pre-shared key for WPA2-PSK or WPA3-SAE-Transition or WPA3-SAE encryption which depends on
+     * the security type.
      */
-    private final @Nullable String mWpa2Passphrase;
+    private final @Nullable String mPassphrase;
 
     /**
      * This is a network that does not broadcast its SSID, so an
@@ -186,20 +193,30 @@
     public static final int SECURITY_TYPE_WPA2_PSK = 1;
 
     /** @hide */
+    @SystemApi
+    public static final int SECURITY_TYPE_WPA3_SAE_TRANSITION = 2;
+
+    /** @hide */
+    @SystemApi
+    public static final int SECURITY_TYPE_WPA3_SAE = 3;
+
+    /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = { "SECURITY_TYPE" }, value = {
+    @IntDef(prefix = { "SECURITY_TYPE_" }, value = {
         SECURITY_TYPE_OPEN,
         SECURITY_TYPE_WPA2_PSK,
+        SECURITY_TYPE_WPA3_SAE_TRANSITION,
+        SECURITY_TYPE_WPA3_SAE,
     })
     public @interface SecurityType {}
 
     /** Private constructor for Builder and Parcelable implementation. */
     private SoftApConfiguration(@Nullable String ssid, @Nullable MacAddress bssid,
-            @Nullable String wpa2Passphrase, boolean hiddenSsid, @BandType int band, int channel,
+            @Nullable String passphrase, boolean hiddenSsid, @BandType int band, int channel,
             @SecurityType int securityType, int maxNumberOfClients) {
         mSsid = ssid;
         mBssid = bssid;
-        mWpa2Passphrase = wpa2Passphrase;
+        mPassphrase = passphrase;
         mHiddenSsid = hiddenSsid;
         mBand = band;
         mChannel = channel;
@@ -218,7 +235,7 @@
         SoftApConfiguration other = (SoftApConfiguration) otherObj;
         return Objects.equals(mSsid, other.mSsid)
                 && Objects.equals(mBssid, other.mBssid)
-                && Objects.equals(mWpa2Passphrase, other.mWpa2Passphrase)
+                && Objects.equals(mPassphrase, other.mPassphrase)
                 && mHiddenSsid == other.mHiddenSsid
                 && mBand == other.mBand
                 && mChannel == other.mChannel
@@ -228,7 +245,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSsid, mBssid, mWpa2Passphrase, mHiddenSsid,
+        return Objects.hash(mSsid, mBssid, mPassphrase, mHiddenSsid,
                 mBand, mChannel, mSecurityType, mMaxNumberOfClients);
     }
 
@@ -237,8 +254,8 @@
         StringBuilder sbuf = new StringBuilder();
         sbuf.append("ssid=").append(mSsid);
         if (mBssid != null) sbuf.append(" \n bssid=").append(mBssid.toString());
-        sbuf.append(" \n Wpa2Passphrase =").append(
-                TextUtils.isEmpty(mWpa2Passphrase) ? "<empty>" : "<non-empty>");
+        sbuf.append(" \n Passphrase =").append(
+                TextUtils.isEmpty(mPassphrase) ? "<empty>" : "<non-empty>");
         sbuf.append(" \n HiddenSsid =").append(mHiddenSsid);
         sbuf.append(" \n Band =").append(mBand);
         sbuf.append(" \n Channel =").append(mChannel);
@@ -251,7 +268,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mSsid);
         dest.writeParcelable(mBssid, flags);
-        dest.writeString(mWpa2Passphrase);
+        dest.writeString(mPassphrase);
         dest.writeBoolean(mHiddenSsid);
         dest.writeInt(mBand);
         dest.writeInt(mChannel);
@@ -299,13 +316,26 @@
         return mBssid;
     }
 
+    // TODO: Remove it after update the caller
     /**
      * Returns String set to be passphrase for the WPA2-PSK AP.
-     * {@link Builder#setWpa2Passphrase(String)}.
+     * {@link #setWpa2Passphrase(String)}.
      */
     @Nullable
     public String getWpa2Passphrase() {
-        return mWpa2Passphrase;
+        if (mSecurityType == SECURITY_TYPE_WPA2_PSK) {
+            return mPassphrase;
+        }
+        return null;
+    }
+
+    /**
+     * Returns String set to be passphrase for current AP.
+     * {@link #setPassphrase(String, @SecurityType int)}.
+     */
+    @Nullable
+    public String getPassphrase() {
+        return mPassphrase;
     }
 
     /**
@@ -360,23 +390,12 @@
     public static final class Builder {
         private String mSsid;
         private MacAddress mBssid;
-        private String mWpa2Passphrase;
+        private String mPassphrase;
         private boolean mHiddenSsid;
         private int mBand;
         private int mChannel;
         private int mMaxNumberOfClients;
-
-        private int setSecurityType() {
-            int securityType = SECURITY_TYPE_OPEN;
-            if (!TextUtils.isEmpty(mWpa2Passphrase)) { // WPA2-PSK network.
-                securityType = SECURITY_TYPE_WPA2_PSK;
-            }
-            return securityType;
-        }
-
-        private void clearAllPassphrase() {
-            mWpa2Passphrase = null;
-        }
+        private int mSecurityType;
 
         /**
          * Constructs a Builder with default values (see {@link Builder}).
@@ -384,11 +403,12 @@
         public Builder() {
             mSsid = null;
             mBssid = null;
-            mWpa2Passphrase = null;
+            mPassphrase = null;
             mHiddenSsid = false;
             mBand = BAND_2GHZ;
             mChannel = 0;
             mMaxNumberOfClients = 0;
+            mSecurityType = SECURITY_TYPE_OPEN;
         }
 
         /**
@@ -399,11 +419,12 @@
 
             mSsid = other.mSsid;
             mBssid = other.mBssid;
-            mWpa2Passphrase = other.mWpa2Passphrase;
+            mPassphrase = other.mPassphrase;
             mHiddenSsid = other.mHiddenSsid;
             mBand = other.mBand;
             mChannel = other.mChannel;
             mMaxNumberOfClients = other.mMaxNumberOfClients;
+            mSecurityType = other.mSecurityType;
         }
 
         /**
@@ -413,8 +434,8 @@
          */
         @NonNull
         public SoftApConfiguration build() {
-            return new SoftApConfiguration(mSsid, mBssid, mWpa2Passphrase,
-                mHiddenSsid, mBand, mChannel, setSecurityType(), mMaxNumberOfClients);
+            return new SoftApConfiguration(mSsid, mBssid, mPassphrase,
+                mHiddenSsid, mBand, mChannel, mSecurityType, mMaxNumberOfClients);
         }
 
         /**
@@ -461,6 +482,7 @@
             return this;
         }
 
+        // TODO: Remove it after update the caller
         /**
          * Specifies that this AP should use WPA2-PSK with the given ASCII WPA2 passphrase.
          * When set to null, an open network is created.
@@ -473,15 +495,47 @@
          */
         @NonNull
         public Builder setWpa2Passphrase(@Nullable String passphrase) {
-            if (passphrase != null) {
+            return setPassphrase(passphrase, SECURITY_TYPE_WPA2_PSK);
+        }
+
+        /**
+         * Specifies that this AP should use specific security type with the given ASCII passphrase.
+         *
+         * @param securityType one of the security types from {@link @SecurityType}.
+         * @param passphrase The passphrase to use for sepcific {@link @SecurityType} configuration
+         * or null with {@link @SecurityType#SECURITY_TYPE_OPEN}.
+         *
+         * @return Builder for chaining.
+         * @throws IllegalArgumentException when the passphrase length is invalid and
+         *         {@code securityType} is not {@link @SecurityType#SECURITY_TYPE_OPEN}
+         *         or non-null passphrase and {@code securityType} is
+         *         {@link @SecurityType#SECURITY_TYPE_OPEN}.
+         */
+        @NonNull
+        public Builder setPassphrase(@Nullable String passphrase, @SecurityType int securityType) {
+            if (securityType == SECURITY_TYPE_OPEN) {
+                if (passphrase != null) {
+                    throw new IllegalArgumentException(
+                            "passphrase should be null when security type is open");
+                }
+            } else {
+                Preconditions.checkStringNotEmpty(passphrase);
                 final CharsetEncoder asciiEncoder = StandardCharsets.US_ASCII.newEncoder();
                 if (!asciiEncoder.canEncode(passphrase)) {
                     throw new IllegalArgumentException("passphrase not ASCII encodable");
                 }
-                Preconditions.checkStringNotEmpty(passphrase);
+                if (securityType == SECURITY_TYPE_WPA2_PSK
+                        || securityType == SECURITY_TYPE_WPA3_SAE_TRANSITION) {
+                    if (passphrase.length() < PSK_MIN_LEN || passphrase.length() > PSK_MAX_LEN) {
+                        throw new IllegalArgumentException(
+                                "Password size must be at least " + PSK_MIN_LEN
+                                + " and no more than " + PSK_MAX_LEN
+                                + " for WPA2_PSK and WPA3_SAE_TRANSITION Mode");
+                    }
+                }
             }
-            clearAllPassphrase();
-            mWpa2Passphrase = passphrase;
+            mSecurityType = securityType;
+            mPassphrase = passphrase;
             return this;
         }
 
diff --git a/wifi/java/android/net/wifi/WifiAnnotations.java b/wifi/java/android/net/wifi/WifiAnnotations.java
index 9223d28..05e5b1d 100644
--- a/wifi/java/android/net/wifi/WifiAnnotations.java
+++ b/wifi/java/android/net/wifi/WifiAnnotations.java
@@ -60,4 +60,45 @@
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Bandwidth {}
+
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+            ScanResult.PROTOCOL_NONE,
+            ScanResult.PROTOCOL_WPA,
+            ScanResult.PROTOCOL_RSN,
+            ScanResult.PROTOCOL_OSEN,
+            ScanResult.PROTOCOL_WAPI
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Protocol {}
+
+    @IntDef(prefix = { "KEY_MGMT_" }, value = {
+        ScanResult.KEY_MGMT_NONE,
+        ScanResult.KEY_MGMT_PSK,
+        ScanResult.KEY_MGMT_EAP,
+        ScanResult.KEY_MGMT_FT_PSK,
+        ScanResult.KEY_MGMT_FT_EAP,
+        ScanResult.KEY_MGMT_PSK_SHA256,
+        ScanResult.KEY_MGMT_EAP_SHA256,
+        ScanResult.KEY_MGMT_OSEN,
+        ScanResult.KEY_MGMT_SAE,
+        ScanResult.KEY_MGMT_OWE,
+        ScanResult.KEY_MGMT_EAP_SUITE_B_192,
+        ScanResult.KEY_MGMT_FT_SAE,
+        ScanResult.KEY_MGMT_OWE_TRANSITION,
+        ScanResult.KEY_MGMT_WAPI_PSK,
+        ScanResult.KEY_MGMT_WAPI_CERT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface KeyMgmt {}
+
+    @IntDef(prefix = { "CIPHER_" }, value = {
+        ScanResult.CIPHER_NONE,
+        ScanResult.CIPHER_NO_GROUP_ADDRESSED,
+        ScanResult.CIPHER_TKIP,
+        ScanResult.CIPHER_CCMP,
+        ScanResult.CIPHER_GCMP_256,
+        ScanResult.CIPHER_SMS4
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Cipher {}
 }
diff --git a/wifi/java/android/net/wifi/WifiClient.java b/wifi/java/android/net/wifi/WifiClient.java
index 3e09580..3794566 100644
--- a/wifi/java/android/net/wifi/WifiClient.java
+++ b/wifi/java/android/net/wifi/WifiClient.java
@@ -22,8 +22,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.Objects;
 
 /** @hide */
@@ -46,7 +44,7 @@
 
     /** @hide */
     public WifiClient(@NonNull MacAddress macAddress) {
-        Preconditions.checkNotNull(macAddress, "mMacAddress must not be null.");
+        Objects.requireNonNull(macAddress, "mMacAddress must not be null.");
 
         this.mMacAddress = macAddress;
     }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index e3a945d..a379c75 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -20,7 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageManager;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.ProxySettings;
@@ -2428,7 +2428,8 @@
         } else if (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
                 || allowedKeyManagement.get(KeyMgmt.IEEE8021X)) {
             key = SSID + KeyMgmt.strings[KeyMgmt.WPA_EAP];
-        } else if (wepKeys[0] != null) {
+        } else if (wepTxKeyIndex >= 0 && wepTxKeyIndex < wepKeys.length
+                && wepKeys[wepTxKeyIndex] != null) {
             key = SSID + "WEP";
         } else if (allowedKeyManagement.get(KeyMgmt.OWE)) {
             key = SSID + KeyMgmt.strings[KeyMgmt.OWE];
@@ -2446,10 +2447,14 @@
         return key;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Get the IpConfiguration object associated with this WifiConfiguration.
+     * @hide
+     */
+    @NonNull
+    @SystemApi
     public IpConfiguration getIpConfiguration() {
-        return mIpConfiguration;
+        return new IpConfiguration(mIpConfiguration);
     }
 
     /**
@@ -2586,9 +2591,8 @@
         return mPasspointManagementObjectTree;
     }
 
-    /** copy constructor {@hide} */
-    @UnsupportedAppUsage
-    public WifiConfiguration(WifiConfiguration source) {
+    /** Copy constructor */
+    public WifiConfiguration(@NonNull WifiConfiguration source) {
         if (source != null) {
             networkId = source.networkId;
             status = source.status;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 7a59a4f..41f7c6e 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -1336,20 +1336,18 @@
     }
 
     /**
-     * If the current authentication method needs SIM card.
-     * @return true if the credential information require SIM card for current authentication
+     * Utility method to determine whether the configuration's authentication method is SIM-based.
+     *
+     * @return true if the credential information requires SIM card for current authentication
      * method, otherwise it returns false.
-     * @hide
      */
-    public boolean requireSimCredential() {
+    public boolean isAuthenticationSimBased() {
         if (mEapMethod == Eap.SIM || mEapMethod == Eap.AKA || mEapMethod == Eap.AKA_PRIME) {
             return true;
         }
         if (mEapMethod == Eap.PEAP) {
-            if (mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA
-                    || mPhase2Method == Phase2.AKA_PRIME) {
-                return true;
-            }
+            return mPhase2Method == Phase2.SIM || mPhase2Method == Phase2.AKA
+                    || mPhase2Method == Phase2.AKA_PRIME;
         }
         return false;
     }
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index f728491..62337cb 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -19,7 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.NetworkInfo.DetailedState;
 import android.net.shared.Inet4AddressUtils;
 import android.os.Build;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 7693f9a..378c67b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -29,8 +29,8 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.net.ConnectivityManager;
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
index be37c22..378549d 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -29,11 +29,11 @@
 import android.util.LruCache;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * {@link INetworkScoreCache} implementation for Wifi Networks.
@@ -290,7 +290,7 @@
          *          This cannot be null.
          */
         public CacheListener(@NonNull Handler handler) {
-            Preconditions.checkNotNull(handler);
+            Objects.requireNonNull(handler);
             mHandler = handler;
         }
 
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 8badcc0..2c39c32a 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -40,7 +40,6 @@
 import android.util.SparseArray;
 
 import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Preconditions;
 import com.android.internal.util.Protocol;
 
 import java.lang.annotation.Retention;
@@ -48,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -840,8 +840,8 @@
     @RequiresPermission(Manifest.permission.NETWORK_STACK)
     public void registerScanListener(@NonNull @CallbackExecutor Executor executor,
             @NonNull ScanListener listener) {
-        Preconditions.checkNotNull(executor, "executor cannot be null");
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(executor, "executor cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = addListener(listener, executor);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -864,7 +864,7 @@
      *  #registerScanListener}
      */
     public void unregisterScanListener(@NonNull ScanListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -894,7 +894,7 @@
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void startBackgroundScan(ScanSettings settings, ScanListener listener,
             WorkSource workSource) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = addListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -913,7 +913,7 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void stopBackgroundScan(ScanListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -962,7 +962,7 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void startScan(ScanSettings settings, ScanListener listener, WorkSource workSource) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = addListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -981,7 +981,7 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public void stopScan(ScanListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -1036,8 +1036,8 @@
      */
     public void startConnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
             PnoScanListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
-        Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
+        Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
         int key = addListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -1058,8 +1058,8 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void startDisconnectedPnoScan(ScanSettings scanSettings, PnoSettings pnoSettings,
             PnoScanListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
-        Preconditions.checkNotNull(pnoSettings, "pnoSettings cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
+        Objects.requireNonNull(pnoSettings, "pnoSettings cannot be null");
         int key = addListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
@@ -1074,7 +1074,7 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public void stopPnoScan(ScanListener listener) {
-        Preconditions.checkNotNull(listener, "listener cannot be null");
+        Objects.requireNonNull(listener, "listener cannot be null");
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java
index 90756d8..704ae81 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index c9bca4f..8fa9c3d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.MacAddress;
 import android.net.wifi.WpsInfo;
 import android.os.Parcel;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
index 98ec208..710175f 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java
@@ -18,7 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
index acf06fb..ededf67 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.java
@@ -16,10 +16,9 @@
 
 package android.net.wifi.p2p;
 
-import android.annotation.UnsupportedAppUsage;
-import android.os.Parcelable;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
-import android.net.wifi.p2p.WifiP2pDevice;
+import android.os.Parcelable;
 import android.text.TextUtils;
 
 import java.util.ArrayList;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index d8c50f2..21f6704 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -17,7 +17,7 @@
 package android.net.wifi.p2p;
 
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
index 10fd09a..cdb2806 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroupList.java
@@ -17,7 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.LruCache;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 6120e4e..3459c94 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -24,7 +24,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.net.NetworkInfo;
 import android.net.wifi.WpsInfo;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java b/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
index 153e03c..d0fe92d 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pProvDiscEvent.java
@@ -16,7 +16,7 @@
 
 package android.net.wifi.p2p;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A class representing a Wi-Fi p2p provisional discovery request/response
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
index 48b0703..a411502 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pWfdInfo.java
@@ -19,7 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
index e32c8e8..0de7ba6 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pDnsSdServiceInfo.java
@@ -16,7 +16,7 @@
 
 package android.net.wifi.p2p.nsd;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.nsd.DnsSdTxtRecord;
 import android.os.Build;
 import android.text.TextUtils;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
index db0bdb8..37b442b 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.java
@@ -16,7 +16,7 @@
 
 package android.net.wifi.p2p.nsd;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
diff --git a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
index 87528c4..68cbb88 100644
--- a/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
+++ b/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.java
@@ -16,7 +16,7 @@
 
 package android.net.wifi.p2p.nsd;
 
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Build;
 import android.os.Parcel;
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 283f2dd..f70bdac 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -1154,4 +1154,68 @@
         mApInterfaceListeners.clear();
         mSendMgmtFrameInProgress.set(false);
     }
+
+    /**
+     * OEM parsed security type
+     */
+    public static class OemSecurityType {
+        /** The protocol defined in {@link android.net.wifi.WifiAnnotations.Protocol}. */
+        public final @WifiAnnotations.Protocol int protocol;
+        /**
+         * Supported key management types defined
+         * in {@link android.net.wifi.WifiAnnotations.KeyMgmt}.
+         */
+        @NonNull public final List<Integer> keyManagement;
+        /**
+         * Supported pairwise cipher types defined
+         * in {@link android.net.wifi.WifiAnnotations.Cipher}.
+         */
+        @NonNull public final List<Integer> pairwiseCipher;
+        /** The group cipher type defined in {@link android.net.wifi.WifiAnnotations.Cipher}. */
+        public final @WifiAnnotations.Cipher int groupCipher;
+        /**
+         * Default constructor for OemSecurityType
+         *
+         * @param protocol The protocol defined in
+         *                 {@link android.net.wifi.WifiAnnotations.Protocol}.
+         * @param keyManagement Supported key management types defined
+         *                      in {@link android.net.wifi.WifiAnnotations.KeyMgmt}.
+         * @param pairwiseCipher Supported pairwise cipher types defined
+         *                       in {@link android.net.wifi.WifiAnnotations.Cipher}.
+         * @param groupCipher The group cipher type defined
+         *                    in {@link android.net.wifi.WifiAnnotations.Cipher}.
+         */
+        public OemSecurityType(
+                @WifiAnnotations.Protocol int protocol,
+                @NonNull List<Integer> keyManagement,
+                @NonNull List<Integer> pairwiseCipher,
+                @WifiAnnotations.Cipher int groupCipher) {
+            this.protocol = protocol;
+            this.keyManagement = (keyManagement != null)
+                ? keyManagement : new ArrayList<Integer>();
+            this.pairwiseCipher = (pairwiseCipher != null)
+                ? pairwiseCipher : new ArrayList<Integer>();
+            this.groupCipher = groupCipher;
+        }
+    }
+
+    /**
+     * OEM information element parser for security types not parsed by the framework.
+     *
+     * The OEM method should use the method inputs {@code id}, {@code idExt}, and {@code bytes}
+     * to perform the parsing. The method should place the results in an OemSecurityType objct.
+     *
+     * @param id The information element id.
+     * @param idExt The information element extension id. This is valid only when id is
+     *        the extension id, {@code 255}.
+     * @param bytes The raw bytes of information element data, 'Element ID' and 'Length' are
+     *              stripped off already.
+     * @return an OemSecurityType object if this IE is parsed successfully, null otherwise.
+     */
+    @Nullable public static OemSecurityType parseOemSecurityTypeElement(
+            int id,
+            int idExt,
+            @NonNull byte[] bytes) {
+        return null;
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
index 1f60103..acd3343 100644
--- a/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/SoftApConfigurationTest.java
@@ -25,8 +25,12 @@
 
 import org.junit.Test;
 
+import java.util.Random;
+
 @SmallTest
 public class SoftApConfigurationTest {
+    private static final String TEST_CHAR_SET_AS_STRING = "abcdefghijklmnopqrstuvwxyz0123456789";
+
     private SoftApConfiguration parcelUnparcel(SoftApConfiguration configIn) {
         Parcel parcel = Parcel.obtain();
         parcel.writeParcelable(configIn, 0);
@@ -37,6 +41,25 @@
         return configOut;
     }
 
+    /**
+     * Helper method to generate random string.
+     *
+     * Note: this method has limited use as a random string generator.
+     * The characters used in this method do no not cover all valid inputs.
+     * @param length number of characters to generate for the string
+     * @return String generated string of random characters
+     */
+    private String generateRandomString(int length) {
+        Random random = new Random();
+        StringBuilder stringBuilder = new StringBuilder(length);
+        int index = -1;
+        while (stringBuilder.length() < length) {
+            index = random.nextInt(TEST_CHAR_SET_AS_STRING.length());
+            stringBuilder.append(TEST_CHAR_SET_AS_STRING.charAt(index));
+        }
+        return stringBuilder.toString();
+    }
+
     @Test
     public void testBasicSettings() {
         SoftApConfiguration original = new SoftApConfiguration.Builder()
@@ -45,7 +68,7 @@
                 .build();
         assertThat(original.getSsid()).isEqualTo("ssid");
         assertThat(original.getBssid()).isEqualTo(MacAddress.fromString("11:22:33:44:55:66"));
-        assertThat(original.getWpa2Passphrase()).isNull();
+        assertThat(original.getPassphrase()).isNull();
         assertThat(original.getSecurityType()).isEqualTo(SoftApConfiguration.SECURITY_TYPE_OPEN);
         assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
         assertThat(original.getChannel()).isEqualTo(0);
@@ -66,9 +89,9 @@
     @Test
     public void testWpa2() {
         SoftApConfiguration original = new SoftApConfiguration.Builder()
-                .setWpa2Passphrase("secretsecret")
+                .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .build();
-        assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+        assertThat(original.getPassphrase()).isEqualTo("secretsecret");
         assertThat(original.getSecurityType()).isEqualTo(
                 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
         assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_2GHZ);
@@ -90,13 +113,12 @@
     @Test
     public void testWpa2WithAllFieldCustomized() {
         SoftApConfiguration original = new SoftApConfiguration.Builder()
-                .setWpa2Passphrase("secretsecret")
-                .setBand(SoftApConfiguration.BAND_ANY)
+                .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .setChannel(149, SoftApConfiguration.BAND_5GHZ)
                 .setHiddenSsid(true)
                 .setMaxNumberOfClients(10)
                 .build();
-        assertThat(original.getWpa2Passphrase()).isEqualTo("secretsecret");
+        assertThat(original.getPassphrase()).isEqualTo("secretsecret");
         assertThat(original.getSecurityType()).isEqualTo(
                 SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
         assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
@@ -114,4 +136,98 @@
         assertThat(copy).isEqualTo(original);
         assertThat(copy.hashCode()).isEqualTo(original.hashCode());
     }
+
+    @Test
+    public void testWpa3Sae() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setPassphrase("secretsecret", SoftApConfiguration.SECURITY_TYPE_WPA3_SAE)
+                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+        assertThat(original.getPassphrase()).isEqualTo("secretsecret");
+        assertThat(original.getSecurityType()).isEqualTo(
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE);
+        assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
+        assertThat(original.getChannel()).isEqualTo(149);
+        assertThat(original.isHiddenSsid()).isEqualTo(true);
+
+
+        SoftApConfiguration unparceled = parcelUnparcel(original);
+        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isEqualTo(original);
+        assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+        SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isEqualTo(original);
+        assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+    }
+
+    @Test
+    public void testWpa3SaeTransition() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setPassphrase("secretsecret",
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+        assertThat(original.getSecurityType()).isEqualTo(
+                SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION);
+        assertThat(original.getPassphrase()).isEqualTo("secretsecret");
+        assertThat(original.getBand()).isEqualTo(SoftApConfiguration.BAND_5GHZ);
+        assertThat(original.getChannel()).isEqualTo(149);
+        assertThat(original.isHiddenSsid()).isEqualTo(true);
+
+
+        SoftApConfiguration unparceled = parcelUnparcel(original);
+        assertThat(unparceled).isNotSameAs(original);
+        assertThat(unparceled).isEqualTo(original);
+        assertThat(unparceled.hashCode()).isEqualTo(original.hashCode());
+
+        SoftApConfiguration copy = new SoftApConfiguration.Builder(original).build();
+        assertThat(copy).isNotSameAs(original);
+        assertThat(copy).isEqualTo(original);
+        assertThat(copy.hashCode()).isEqualTo(original.hashCode());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidShortPasswordLengthForWpa2() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1),
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidLongPasswordLengthForWpa2() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1),
+                        SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
+                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidShortPasswordLengthForWpa3SaeTransition() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MIN_LEN - 1),
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalidLongPasswordLengthForWpa3SaeTransition() {
+        SoftApConfiguration original = new SoftApConfiguration.Builder()
+                .setPassphrase(generateRandomString(SoftApConfiguration.PSK_MAX_LEN + 1),
+                        SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION)
+                .setChannel(149, SoftApConfiguration.BAND_5GHZ)
+                .setHiddenSsid(true)
+                .build();
+    }
+
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 5d6549e..909cfef 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -270,7 +270,23 @@
         config.allowedKeyManagement.clear();
         assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString());
 
+        // set WEP key and give a valid index.
         config.wepKeys[0] = null;
+        config.wepKeys[2] = "TestWep";
+        config.wepTxKeyIndex = 2;
+        config.allowedKeyManagement.clear();
+        assertEquals(mSsid + "WEP", config.getSsidAndSecurityTypeString());
+
+        // set WEP key but does not give a valid index.
+        config.wepKeys[0] = null;
+        config.wepKeys[2] = "TestWep";
+        config.wepTxKeyIndex = 0;
+        config.allowedKeyManagement.clear();
+        config.allowedKeyManagement.set(KeyMgmt.OWE);
+        assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString());
+
+        config.wepKeys[0] = null;
+        config.wepTxKeyIndex = 0;
         config.allowedKeyManagement.clear();
         config.allowedKeyManagement.set(KeyMgmt.OWE);
         assertEquals(mSsid + KeyMgmt.strings[KeyMgmt.OWE], config.getSsidAndSecurityTypeString());