Merge "Add DisplayContent to hierarchy after stubbing."
diff --git a/Android.bp b/Android.bp
index 25c643d..a10f8de 100644
--- a/Android.bp
+++ b/Android.bp
@@ -284,7 +284,7 @@
     name: "framework-aidl-export-defaults",
     aidl: {
         export_include_dirs: [
-            "apex/media/java",
+            "apex/media/framework/java",
             "core/java",
             "drm/java",
             "graphics/java",
@@ -632,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",
     ],
 }
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/media/Android.bp b/apex/media/framework/Android.bp
similarity index 100%
rename from apex/media/Android.bp
rename to apex/media/framework/Android.bp
diff --git a/apex/media/jarjar_rules.txt b/apex/media/framework/jarjar_rules.txt
similarity index 100%
rename from apex/media/jarjar_rules.txt
rename to apex/media/framework/jarjar_rules.txt
diff --git a/apex/media/java/android/media/BufferingParams.java b/apex/media/framework/java/android/media/BufferingParams.java
similarity index 100%
rename from apex/media/java/android/media/BufferingParams.java
rename to apex/media/framework/java/android/media/BufferingParams.java
diff --git a/apex/media/java/android/media/Controller2Link.aidl b/apex/media/framework/java/android/media/Controller2Link.aidl
similarity index 100%
rename from apex/media/java/android/media/Controller2Link.aidl
rename to apex/media/framework/java/android/media/Controller2Link.aidl
diff --git a/apex/media/java/android/media/Controller2Link.java b/apex/media/framework/java/android/media/Controller2Link.java
similarity index 100%
rename from apex/media/java/android/media/Controller2Link.java
rename to apex/media/framework/java/android/media/Controller2Link.java
diff --git a/apex/media/java/android/media/DataSourceCallback.java b/apex/media/framework/java/android/media/DataSourceCallback.java
similarity index 100%
rename from apex/media/java/android/media/DataSourceCallback.java
rename to apex/media/framework/java/android/media/DataSourceCallback.java
diff --git a/apex/media/java/android/media/IMediaController2.aidl b/apex/media/framework/java/android/media/IMediaController2.aidl
similarity index 100%
rename from apex/media/java/android/media/IMediaController2.aidl
rename to apex/media/framework/java/android/media/IMediaController2.aidl
diff --git a/apex/media/java/android/media/IMediaSession2.aidl b/apex/media/framework/java/android/media/IMediaSession2.aidl
similarity index 100%
rename from apex/media/java/android/media/IMediaSession2.aidl
rename to apex/media/framework/java/android/media/IMediaSession2.aidl
diff --git a/apex/media/java/android/media/IMediaSession2Service.aidl b/apex/media/framework/java/android/media/IMediaSession2Service.aidl
similarity index 100%
rename from apex/media/java/android/media/IMediaSession2Service.aidl
rename to apex/media/framework/java/android/media/IMediaSession2Service.aidl
diff --git a/apex/media/java/android/media/MediaConstants.java b/apex/media/framework/java/android/media/MediaConstants.java
similarity index 100%
rename from apex/media/java/android/media/MediaConstants.java
rename to apex/media/framework/java/android/media/MediaConstants.java
diff --git a/apex/media/java/android/media/MediaController2.java b/apex/media/framework/java/android/media/MediaController2.java
similarity index 100%
rename from apex/media/java/android/media/MediaController2.java
rename to apex/media/framework/java/android/media/MediaController2.java
diff --git a/apex/media/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
similarity index 100%
rename from apex/media/java/android/media/MediaParser.java
rename to apex/media/framework/java/android/media/MediaParser.java
diff --git a/apex/media/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
similarity index 100%
rename from apex/media/java/android/media/MediaSession2.java
rename to apex/media/framework/java/android/media/MediaSession2.java
diff --git a/apex/media/java/android/media/MediaSession2Service.java b/apex/media/framework/java/android/media/MediaSession2Service.java
similarity index 100%
rename from apex/media/java/android/media/MediaSession2Service.java
rename to apex/media/framework/java/android/media/MediaSession2Service.java
diff --git a/apex/media/java/android/media/ProxyDataSourceCallback.java b/apex/media/framework/java/android/media/ProxyDataSourceCallback.java
similarity index 100%
rename from apex/media/java/android/media/ProxyDataSourceCallback.java
rename to apex/media/framework/java/android/media/ProxyDataSourceCallback.java
diff --git a/apex/media/java/android/media/RoutingDelegate.java b/apex/media/framework/java/android/media/RoutingDelegate.java
similarity index 100%
rename from apex/media/java/android/media/RoutingDelegate.java
rename to apex/media/framework/java/android/media/RoutingDelegate.java
diff --git a/apex/media/java/android/media/Session2Command.aidl b/apex/media/framework/java/android/media/Session2Command.aidl
similarity index 100%
rename from apex/media/java/android/media/Session2Command.aidl
rename to apex/media/framework/java/android/media/Session2Command.aidl
diff --git a/apex/media/java/android/media/Session2Command.java b/apex/media/framework/java/android/media/Session2Command.java
similarity index 100%
rename from apex/media/java/android/media/Session2Command.java
rename to apex/media/framework/java/android/media/Session2Command.java
diff --git a/apex/media/java/android/media/Session2CommandGroup.java b/apex/media/framework/java/android/media/Session2CommandGroup.java
similarity index 100%
rename from apex/media/java/android/media/Session2CommandGroup.java
rename to apex/media/framework/java/android/media/Session2CommandGroup.java
diff --git a/apex/media/java/android/media/Session2Link.java b/apex/media/framework/java/android/media/Session2Link.java
similarity index 100%
rename from apex/media/java/android/media/Session2Link.java
rename to apex/media/framework/java/android/media/Session2Link.java
diff --git a/apex/media/java/android/media/Session2Token.aidl b/apex/media/framework/java/android/media/Session2Token.aidl
similarity index 100%
rename from apex/media/java/android/media/Session2Token.aidl
rename to apex/media/framework/java/android/media/Session2Token.aidl
diff --git a/apex/media/java/android/media/Session2Token.java b/apex/media/framework/java/android/media/Session2Token.java
similarity index 100%
rename from apex/media/java/android/media/Session2Token.java
rename to apex/media/framework/java/android/media/Session2Token.java
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 e4f7300..d57afee 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -50,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;
@@ -85,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;
@@ -203,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.
@@ -454,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++) {
@@ -770,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 {
@@ -786,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 {
@@ -798,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
@@ -817,7 +736,7 @@
 
     @Override // Binder call
     public void cancelAlarmForSubscriberTriggering() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Cancelling periodic alarm");
         }
@@ -831,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()));
@@ -849,7 +768,7 @@
 
     @Override // Binder call
     public void cancelPullingAlarm() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Cancelling pulling alarm");
         }
@@ -2455,7 +2374,7 @@
      */
     @Override // Binder call
     public StatsLogEventWrapper[] pullData(int tagId) {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         if (DEBUG) {
             Slog.d(TAG, "Pulling " + tagId);
         }
@@ -2690,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);
@@ -2703,7 +2621,7 @@
 
     @Override
     public void triggerUidSnapshot() {
-        enforceCallingPermission();
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
         synchronized (sStatsdLock) {
             final long token = Binder.clearCallingIdentity();
             try {
@@ -2716,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) {
@@ -2817,6 +2728,7 @@
                                 + "alive.");
                 return;
             }
+            mStatsManagerService.statsdReady(sStatsd);
             if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
             try {
                 sStatsd.statsCompanionReady();
@@ -2928,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 24b7978..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,37 +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"));
-            if (sStatsd == null) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Failed to get stats service.");
-                }
-                return;
+            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()));
             }
-            // Assume statsd is ready since this is called form statscompanion, link to statsd.
+        }
+        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 9425b81..ac69f6f 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
@@ -7126,6 +7128,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
@@ -29237,6 +29240,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();
@@ -29248,6 +29252,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);
@@ -30236,6 +30241,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();
@@ -36090,6 +36096,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);
@@ -41783,6 +41820,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();
@@ -41792,6 +41830,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 {
@@ -41880,7 +41920,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);
@@ -41909,6 +41948,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();
@@ -43124,6 +43172,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;
@@ -45544,6 +45593,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);
@@ -45561,6 +45611,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
@@ -45636,6 +45687,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
   }
 
@@ -45644,7 +45696,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 6b2aea5..766daab 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";
@@ -364,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";
@@ -413,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);
@@ -446,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);
@@ -454,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();
@@ -1268,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);
@@ -1697,6 +1712,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";
@@ -4139,6 +4155,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 {
@@ -4331,6 +4355,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();
@@ -4445,12 +4478,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);
@@ -5627,8 +5662,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 {
@@ -6368,6 +6427,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);
@@ -6388,6 +6448,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();
@@ -7731,6 +7799,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";
@@ -7758,7 +7829,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 {
@@ -7989,6 +8075,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 {
@@ -9548,7 +9639,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
@@ -9789,6 +9882,10 @@
     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();
@@ -9931,6 +10028,8 @@
   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 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);
@@ -10058,13 +10157,14 @@
     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);
@@ -10080,6 +10180,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);
@@ -10087,6 +10188,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);
@@ -10105,10 +10207,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";
@@ -10170,6 +10275,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);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 1c1d794..7ba943c 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
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/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/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/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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c942a46..4a8e4e2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -850,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";
@@ -1144,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;
@@ -1221,6 +1227,7 @@
             OP_MANAGE_IPSEC_TUNNELS,
             OP_INSTANT_APP_START_FOREGROUND,
             OP_MANAGE_EXTERNAL_STORAGE,
+            OP_INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1325,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
     };
 
     /**
@@ -1424,6 +1432,7 @@
             OPSTR_ACCESS_MEDIA_LOCATION,
             OPSTR_QUERY_ALL_PACKAGES,
             OPSTR_MANAGE_EXTERNAL_STORAGE,
+            OPSTR_INTERACT_ACROSS_PROFILES,
     };
 
     /**
@@ -1523,7 +1532,8 @@
             "READ_DEVICE_IDENTIFIERS",
             "ACCESS_MEDIA_LOCATION",
             "QUERY_ALL_PACKAGES",
-            "MANAGE_EXTERNAL_STORAGE"
+            "MANAGE_EXTERNAL_STORAGE",
+            "INTERACT_ACROSS_PROFILES"
     };
 
     /**
@@ -1625,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,
     };
 
     /**
@@ -1726,6 +1737,7 @@
             null, // ACCESS_MEDIA_LOCATION
             null, // QUERY_ALL_PACKAGES
             null, // MANAGE_EXTERNAL_STORAGE
+            null, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1826,6 +1838,7 @@
             false, // ACCESS_MEDIA_LOCATION
             false, // QUERY_ALL_PACKAGES
             false, // MANAGE_EXTERNAL_STORAGE
+            false, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -1925,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
     };
 
     /**
@@ -2028,6 +2042,7 @@
             false, // ACCESS_MEDIA_LOCATION
             false, // QUERY_ALL_PACKAGES
             false, // MANAGE_EXTERNAL_STORAGE
+            false, // INTERACT_ACROSS_PROFILES
     };
 
     /**
@@ -3902,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.
      *
@@ -3917,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;
@@ -3943,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;
@@ -3976,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;
             }
 
@@ -3987,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;
             }
 
@@ -4005,6 +4097,13 @@
                     }
                 }
                 mOpNames = opNames;
+
+                if (mOpNames == null) {
+                    mFilter &= ~FILTER_BY_OP_NAMES;
+                } else {
+                    mFilter |= FILTER_BY_OP_NAMES;
+                }
+
                 return this;
             }
 
@@ -4029,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);
             }
         }
     }
@@ -4187,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);
@@ -4205,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);
                 }
             }
         }
@@ -4236,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 */
@@ -4534,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);
                 }
             }
         }
@@ -4559,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);
         }
 
         /**
@@ -4718,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) {
@@ -4727,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);
@@ -4738,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<>();
@@ -4762,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);
@@ -4778,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);
@@ -4817,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.
@@ -4865,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);
@@ -4897,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
+
     }
 
     /**
@@ -5273,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
@@ -5606,9 +6122,9 @@
         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 {
@@ -5646,8 +6162,8 @@
         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 {
@@ -7488,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/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index c3ef3ad..d993ec1 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@
     };
 
     @DataClass.Generated(
-            time = 1576864422226L,
+            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 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() {}
 
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/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/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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c9060c4..d3132d8 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;
@@ -11207,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>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9c82ff6..08c5dff 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -444,6 +444,8 @@
     void setCrossProfilePackages(in ComponentName admin, in List<String> packageNames);
     List<String> getCrossProfilePackages(in ComponentName admin);
 
+    List<String> getAllCrossProfilePackages();
+
     boolean isManagedKiosk();
     boolean isUnattendedManagedKiosk();
 
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/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/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/app/timedetector/NetworkTimeSuggestion.aidl b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
new file mode 100644
index 0000000..731c907
--- /dev/null
+++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.timedetector;
+
+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/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/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/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java
index 7b24d3d..5956857 100644
--- a/core/java/android/content/pm/parsing/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java
@@ -465,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() {
@@ -842,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() {
@@ -850,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() {
diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java
index 0a8547a..3effc5a 100644
--- a/core/java/android/database/AbstractCursor.java
+++ b/core/java/android/database/AbstractCursor.java
@@ -17,7 +17,7 @@
 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;
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 cad4e81..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;
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 0608cb3..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;
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/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/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 248c145..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;
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/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..b808088 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,7 +44,6 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 import java.util.UUID;
 
 /**
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/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 9636b13..a0f64cd 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -18,14 +18,13 @@
 
 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;
 
 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 ffb97c0..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;
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 fbd90bd..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;
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..431773d 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;
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 3d44944..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);
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/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/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/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/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 c95e132..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;
@@ -4105,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.
@@ -4366,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/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/autofill/InlinePresentation.aidl b/core/java/android/service/autofill/InlinePresentation.aidl
new file mode 100644
index 0000000..eeddcc0
--- /dev/null
+++ b/core/java/android/service/autofill/InlinePresentation.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.autofill;
+
+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/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/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 1b2feda..b94475a 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -614,9 +614,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 +675,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/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/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index f8bcb00..a22f5a5 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -72,12 +72,6 @@
             "android.intent.action.USER_ACTIVITY_NOTIFICATION";
 
     /**
-     * Broadcast sent when a (custom) bugreport is requested.
-     */
-    String ACTION_CUSTOM_BUGREPORT_REQUESTED =
-            "android.intent.action.CUSTOM_BUGREPORT_REQUESTED";
-
-    /**
      * Sticky broadcast of the current HDMI plugged state.
      */
     String ACTION_HDMI_PLUGGED = "android.intent.action.HDMI_PLUGGED";
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0340cb3..a0cf534 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1462,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) {
@@ -2424,7 +2428,9 @@
         return mSelectionControllerEnabled;
     }
 
-    private InsertionPointCursorController getInsertionController() {
+    /** Returns the controller for the insertion cursor. */
+    @VisibleForTesting
+    public @Nullable InsertionPointCursorController getInsertionController() {
         if (!mInsertionControllerEnabled) {
             return null;
         }
@@ -2439,8 +2445,9 @@
         return mInsertionPointCursorController;
     }
 
-    @Nullable
-    SelectionModifierCursorController getSelectionController() {
+    /** Returns the controller for selection. */
+    @VisibleForTesting
+    public @Nullable SelectionModifierCursorController getSelectionController() {
         if (!mSelectionControllerEnabled) {
             return null;
         }
@@ -5723,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;
@@ -5738,7 +5750,8 @@
                     } else if (FLAG_ENABLE_CURSOR_DRAG
                                 && mTextView.getLayout() != null
                                 && mTextView.isFocused()
-                                && mTouchState.isMovedEnoughForDrag()) {
+                                && mTouchState.isMovedEnoughForDrag()
+                                && !mTouchState.isDragCloseToVertical()) {
                         startCursorDrag(event);
                     }
                     break;
@@ -5899,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;
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/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/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 1cc6d78..b6b548c 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -2434,11 +2434,6 @@
         FooterViewHolder(View itemView) {
             super(itemView);
         }
-
-        public void setHeight(int height) {
-            itemView.setLayoutParams(
-                    new RecyclerView.LayoutParams(LayoutParams.MATCH_PARENT, height));
-        }
     }
 
     /**
@@ -2471,7 +2466,7 @@
 
         private boolean mLayoutRequested = false;
 
-        private FooterViewHolder mFooterViewHolder;
+        private int mFooterHeight = 0;
 
         private static final int VIEW_TYPE_DIRECT_SHARE = 0;
         private static final int VIEW_TYPE_NORMAL = 1;
@@ -2490,9 +2485,6 @@
             mChooserListAdapter = wrappedAdapter;
             mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
 
-            mFooterViewHolder = new FooterViewHolder(
-                    new Space(ChooserActivity.this.getApplicationContext()));
-
             mShowAzLabelIfPoss = getNumSheetExpansions() < NUM_EXPANSIONS_TO_HIDE_AZ_LABEL;
 
             wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
@@ -2511,7 +2503,7 @@
         }
 
         public void setFooterHeight(int height) {
-            mFooterViewHolder.setHeight(height);
+            mFooterHeight = height;
         }
 
         /**
@@ -2614,7 +2606,10 @@
                 case VIEW_TYPE_CALLER_AND_RANK:
                     return createItemGroupViewHolder(viewType, parent);
                 case VIEW_TYPE_FOOTER:
-                    return mFooterViewHolder;
+                    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;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 4e764fc..dabaf5a 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -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/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index f7b7742..5e3c5df 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -24,6 +24,7 @@
 import static android.os.Process.PROC_SPACE_TERM;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.os.CpuUsageProto;
 import android.os.Process;
 import android.os.StrictMode;
 import android.os.SystemClock;
@@ -31,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;
@@ -740,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 179828c..a86d702 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -35,6 +35,7 @@
 import com.android.server.NetworkManagementSocketTagger;
 
 import dalvik.system.RuntimeHooks;
+import dalvik.system.ThreadPrioritySetter;
 import dalvik.system.VMRuntime;
 
 import libcore.content.type.MimeMap;
@@ -204,6 +205,7 @@
      */
     public static void preForkInit() {
         if (DEBUG) Slog.d(TAG, "Entered preForkInit.");
+        RuntimeHooks.setThreadPrioritySetter(new RuntimeThreadPrioritySetter());
         RuntimeInit.enableDdms();
         // TODO(b/142019040#comment13): Decide whether to load the default instance eagerly, i.e.
         // MimeMap.setDefault(DefaultMimeMapFactory.create());
@@ -216,6 +218,35 @@
         MimeMap.setDefaultSupplier(DefaultMimeMapFactory::create);
     }
 
+    private static class RuntimeThreadPrioritySetter implements ThreadPrioritySetter {
+        // Should remain consistent with kNiceValues[] in system/libartpalette/palette_android.cc
+        private static final int[] NICE_VALUES = {
+            Process.THREAD_PRIORITY_LOWEST,  // 1 (MIN_PRIORITY)
+            Process.THREAD_PRIORITY_BACKGROUND + 6,
+            Process.THREAD_PRIORITY_BACKGROUND + 3,
+            Process.THREAD_PRIORITY_BACKGROUND,
+            Process.THREAD_PRIORITY_DEFAULT,  // 5 (NORM_PRIORITY)
+            Process.THREAD_PRIORITY_DEFAULT - 2,
+            Process.THREAD_PRIORITY_DEFAULT - 4,
+            Process.THREAD_PRIORITY_URGENT_DISPLAY + 3,
+            Process.THREAD_PRIORITY_URGENT_DISPLAY + 2,
+            Process.THREAD_PRIORITY_URGENT_DISPLAY  // 10 (MAX_PRIORITY)
+        };
+
+        @Override
+        public void setPriority(int priority) {
+            // Check NICE_VALUES[] length first.
+            if (NICE_VALUES.length != (1 + Thread.MAX_PRIORITY - Thread.MIN_PRIORITY)) {
+                throw new AssertionError("Unexpected NICE_VALUES.length=" + NICE_VALUES.length);
+            }
+            // Priority should be in the range of MIN_PRIORITY (1) to MAX_PRIORITY (10).
+            if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
+                throw new IllegalArgumentException("Priority out of range: " + priority);
+            }
+            Process.setThreadPriority(NICE_VALUES[priority - Thread.MIN_PRIORITY]);
+        }
+    }
+
     @UnsupportedAppUsage
     protected static final void commonInit() {
         if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
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..6933f16 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -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/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/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/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 4314eb6..566c385 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -237,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;
@@ -407,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) {
@@ -462,7 +464,7 @@
 
     jint* otherArray = (jint*)env->GetPrimitiveArrayCritical(otherIntArray, 0);
     if (otherArray == NULL) {
-        return;
+        return JNI_FALSE;
     }
 
     int j=0;
@@ -479,6 +481,7 @@
     }
 
     env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
+    return JNI_TRUE;
 }
 
 static void android_os_Debug_getDirtyPages(JNIEnv *env, jobject clazz, jobject object)
@@ -508,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) {
@@ -866,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 },
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/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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1c48a74..31faff6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -96,7 +96,6 @@
     <protected-broadcast android:name="android.intent.action.USER_ACTIVITY_NOTIFICATION" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" />
-    <protected-broadcast android:name="android.intent.action.CUSTOM_BUGREPORT_REQUESTED" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -2347,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
@@ -2549,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"
@@ -2567,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   -->
     <!-- ==================================================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 57f99a9..e839709 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,9 +4284,4 @@
 
     <!-- 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>
 </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 d12d069..817ccde 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3007,6 +3007,7 @@
       <public name="preferMinimalPostProcessing"/>
       <public name="featureId" />
       <public name="supportsInlineSuggestions" />
+      <public name="crossProfile" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2a17548..129c862 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" />
@@ -3800,5 +3800,5 @@
   <!-- Assistant handles -->
   <java-symbol type="dimen" name="assist_handle_shadow_radius" />
 
-  <java-symbol type="bool" name="config_customBugreport" />
+
 </resources>
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/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/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/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 38a9f46..eb1d1ab 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -160,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/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index aecef8e..bcb313e 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -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/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/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 9100032..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)) {
@@ -596,7 +597,9 @@
 
         if (sessionInfo != null) {
             RouteSessionController controller = new RouteSessionController(sessionInfo);
-            mSessionControllers.put(controller.getUniqueSessionId(), controller);
+            synchronized (sRouterLock) {
+                mSessionControllers.put(controller.getUniqueSessionId(), controller);
+            }
             notifySessionCreated(controller);
         }
     }
@@ -607,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="
@@ -627,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(
@@ -671,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.
      */
@@ -737,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) {}
     }
 
     /**
@@ -755,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;
@@ -771,7 +823,7 @@
          * @return the ID of the session
          */
         public int getSessionId() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getSessionId();
             }
         }
@@ -782,7 +834,7 @@
          */
         @NonNull
         public String getUniqueSessionId() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getUniqueSessionId();
             }
         }
@@ -792,7 +844,7 @@
          */
         @NonNull
         public String getControlCategory() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getControlCategory();
             }
         }
@@ -802,7 +854,7 @@
          */
         @Nullable
         public Bundle getControlHints() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return mSessionInfo.getControlHints();
             }
         }
@@ -812,7 +864,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getSelectedRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getSelectedRoutes());
             }
         }
@@ -822,7 +874,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getSelectableRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getSelectableRoutes());
             }
         }
@@ -832,7 +884,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getDeselectableRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getDeselectableRoutes());
             }
         }
@@ -842,7 +894,7 @@
          */
         @NonNull
         public List<MediaRoute2Info> getTransferrableRoutes() {
-            synchronized (mLock) {
+            synchronized (mControllerLock) {
                 return getRoutesWithIdsLocked(mSessionInfo.getTransferrableRoutes());
             }
         }
@@ -853,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;
             }
         }
@@ -876,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())) {
@@ -890,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);
                 }
@@ -917,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())) {
@@ -931,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);
                 }
@@ -958,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())) {
@@ -973,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);
                 }
@@ -986,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));
@@ -1139,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/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/FrontendInfo.java b/media/java/android/media/tv/tuner/FrontendInfo.java
new file mode 100644
index 0000000..2ab100d
--- /dev/null
+++ b/media/java/android/media/tv/tuner/FrontendInfo.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerConstants.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/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index b8ab7ee..ad7d9b4 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -21,6 +21,7 @@
 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.FilterSubtype;
 import android.media.tv.tuner.TunerConstants.FilterType;
@@ -29,10 +30,12 @@
 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.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 
+import java.io.FileDescriptor;
 import java.util.List;
 
 /**
@@ -110,6 +113,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,6 +127,8 @@
 
     private native Dvr nativeOpenDvr(int type, int bufferSize);
 
+    private static native DemuxCapabilities nativeGetDemuxCapabilities();
+
     /**
      * Frontend Callback.
      *
@@ -351,6 +361,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;
@@ -379,7 +469,8 @@
         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();
@@ -405,10 +496,11 @@
          * @param settings the settings of the filter.
          * @return result status of the operation.
          */
-        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);
         }
@@ -708,6 +800,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 +877,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..e8e4b22 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -26,143 +26,207 @@
 /**
  * @hide
  */
-final class TunerConstants {
+public final class TunerConstants {
     public static final int INVALID_TS_PID = Constants.Constant.INVALID_TS_PID;
     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;
 
-    @Retention(RetentionPolicy.SOURCE)
+    /** @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 +238,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 +588,621 @@
             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;
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_ANALOG_TYPE_UNDEFINED, FRONTEND_ANALOG_TYPE_PAL, FRONTEND_ANALOG_TYPE_SECAM,
+            FRONTEND_ANALOG_TYPE_NTSC})
+    public @interface FrontendAnalogType {}
+
+    public static final int FRONTEND_ANALOG_TYPE_UNDEFINED = Constants.FrontendAnalogType.UNDEFINED;
+    public static final int FRONTEND_ANALOG_TYPE_PAL = Constants.FrontendAnalogType.PAL;
+    public static final int FRONTEND_ANALOG_TYPE_SECAM = Constants.FrontendAnalogType.SECAM;
+    public static final int FRONTEND_ANALOG_TYPE_NTSC = Constants.FrontendAnalogType.NTSC;
 
     @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendAnalogSifStandard {}
+
+    public static final int FRONTEND_ANALOG_SIF_UNDEFINED =
+            Constants.FrontendAnalogSifStandard.UNDEFINED;
+    public static final int FRONTEND_ANALOG_SIF_BG = Constants.FrontendAnalogSifStandard.BG;
+    public static final int FRONTEND_ANALOG_SIF_BG_A2 = Constants.FrontendAnalogSifStandard.BG_A2;
+    public static final int FRONTEND_ANALOG_SIF_BG_NICAM =
+            Constants.FrontendAnalogSifStandard.BG_NICAM;
+    public static final int FRONTEND_ANALOG_SIF_I = Constants.FrontendAnalogSifStandard.I;
+    public static final int FRONTEND_ANALOG_SIF_DK = Constants.FrontendAnalogSifStandard.DK;
+    public static final int FRONTEND_ANALOG_SIF_DK1 = Constants.FrontendAnalogSifStandard.DK1;
+    public static final int FRONTEND_ANALOG_SIF_DK2 = Constants.FrontendAnalogSifStandard.DK2;
+    public static final int FRONTEND_ANALOG_SIF_DK3 = Constants.FrontendAnalogSifStandard.DK3;
+    public static final int FRONTEND_ANALOG_SIF_DK_NICAM =
+            Constants.FrontendAnalogSifStandard.DK_NICAM;
+    public static final int FRONTEND_ANALOG_SIF_L = Constants.FrontendAnalogSifStandard.L;
+    public static final int FRONTEND_ANALOG_SIF_M = Constants.FrontendAnalogSifStandard.M;
+    public static final int FRONTEND_ANALOG_SIF_M_BTSC = Constants.FrontendAnalogSifStandard.M_BTSC;
+    public static final int FRONTEND_ANALOG_SIF_M_A2 = Constants.FrontendAnalogSifStandard.M_A2;
+    public static final int FRONTEND_ANALOG_SIF_M_EIA_J =
+            Constants.FrontendAnalogSifStandard.M_EIA_J;
+    public static final int FRONTEND_ANALOG_SIF_I_NICAM =
+            Constants.FrontendAnalogSifStandard.I_NICAM;
+    public static final int FRONTEND_ANALOG_SIF_L_NICAM =
+            Constants.FrontendAnalogSifStandard.L_NICAM;
+    public static final int FRONTEND_ANALOG_SIF_L_PRIME =
+            Constants.FrontendAnalogSifStandard.L_PRIME;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_ATSC_MODULATION_UNDEFINED, FRONTEND_ATSC_MODULATION_AUTO,
+            FRONTEND_ATSC_MODULATION_MOD_8VSB, FRONTEND_ATSC_MODULATION_MOD_16VSB})
+    public @interface FrontendAtscModulation {}
+
+    public static final int FRONTEND_ATSC_MODULATION_UNDEFINED =
+            Constants.FrontendAtscModulation.UNDEFINED;
+    public static final int FRONTEND_ATSC_MODULATION_AUTO = Constants.FrontendAtscModulation.AUTO;
+    public static final int FRONTEND_ATSC_MODULATION_MOD_8VSB =
+            Constants.FrontendAtscModulation.MOD_8VSB;
+    public static final int FRONTEND_ATSC_MODULATION_MOD_16VSB =
+            Constants.FrontendAtscModulation.MOD_16VSB;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_ATSC3_BANDWIDTH_UNDEFINED, FRONTEND_ATSC3_BANDWIDTH_AUTO,
+            FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_6MHZ, FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_7MHZ,
+            FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_8MHZ})
+    public @interface FrontendAtsc3Bandwidth {}
+
+    public static final int FRONTEND_ATSC3_BANDWIDTH_UNDEFINED =
+            Constants.FrontendAtsc3Bandwidth.UNDEFINED;
+    public static final int FRONTEND_ATSC3_BANDWIDTH_AUTO = Constants.FrontendAtsc3Bandwidth.AUTO;
+    public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_6MHZ =
+            Constants.FrontendAtsc3Bandwidth.BANDWIDTH_6MHZ;
+    public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_7MHZ =
+            Constants.FrontendAtsc3Bandwidth.BANDWIDTH_7MHZ;
+    public static final int FRONTEND_ATSC3_BANDWIDTH_BANDWIDTH_8MHZ =
+            Constants.FrontendAtsc3Bandwidth.BANDWIDTH_8MHZ;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendAtsc3Modulation {}
+
+    public static final int FRONTEND_ATSC3_MODULATION_UNDEFINED =
+            Constants.FrontendAtsc3Modulation.UNDEFINED;
+    public static final int FRONTEND_ATSC3_MODULATION_AUTO = Constants.FrontendAtsc3Modulation.AUTO;
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_QPSK =
+            Constants.FrontendAtsc3Modulation.MOD_QPSK;
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_16QAM =
+            Constants.FrontendAtsc3Modulation.MOD_16QAM;
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_64QAM =
+            Constants.FrontendAtsc3Modulation.MOD_64QAM;
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_256QAM =
+            Constants.FrontendAtsc3Modulation.MOD_256QAM;
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_1024QAM =
+            Constants.FrontendAtsc3Modulation.MOD_1024QAM;
+    public static final int FRONTEND_ATSC3_MODULATION_MOD_4096QAM =
+            Constants.FrontendAtsc3Modulation.MOD_4096QAM;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendAtsc3TimeInterleaveMode {}
+
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_UNDEFINED =
+            Constants.FrontendAtsc3TimeInterleaveMode.UNDEFINED;
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_AUTO =
+            Constants.FrontendAtsc3TimeInterleaveMode.AUTO;
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_CTI =
+            Constants.FrontendAtsc3TimeInterleaveMode.CTI;
+    public static final int FRONTEND_ATSC3_TIME_INTERLEAVE_MODE_HTI =
+            Constants.FrontendAtsc3TimeInterleaveMode.HTI;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendAtsc3CodeRate {}
+
+    public static final int FRONTEND_ATSC3_CODERATE_UNDEFINED =
+            Constants.FrontendAtsc3CodeRate.UNDEFINED;
+    public static final int FRONTEND_ATSC3_CODERATE_AUTO = Constants.FrontendAtsc3CodeRate.AUTO;
+    public static final int FRONTEND_ATSC3_CODERATE_2_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_2_15;
+    public static final int FRONTEND_ATSC3_CODERATE_3_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_3_15;
+    public static final int FRONTEND_ATSC3_CODERATE_4_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_4_15;
+    public static final int FRONTEND_ATSC3_CODERATE_5_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_5_15;
+    public static final int FRONTEND_ATSC3_CODERATE_6_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_6_15;
+    public static final int FRONTEND_ATSC3_CODERATE_7_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_7_15;
+    public static final int FRONTEND_ATSC3_CODERATE_8_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_8_15;
+    public static final int FRONTEND_ATSC3_CODERATE_9_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_9_15;
+    public static final int FRONTEND_ATSC3_CODERATE_10_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_10_15;
+    public static final int FRONTEND_ATSC3_CODERATE_11_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_11_15;
+    public static final int FRONTEND_ATSC3_CODERATE_12_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_12_15;
+    public static final int FRONTEND_ATSC3_CODERATE_13_15 =
+            Constants.FrontendAtsc3CodeRate.CODERATE_13_15;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendAtsc3Fec {}
+
+    public static final int FRONTEND_ATSC3_FEC_UNDEFINED = Constants.FrontendAtsc3Fec.UNDEFINED;
+    public static final int FRONTEND_ATSC3_FEC_AUTO = Constants.FrontendAtsc3Fec.AUTO;
+    public static final int FRONTEND_ATSC3_FEC_BCH_LDPC_16K =
+            Constants.FrontendAtsc3Fec.BCH_LDPC_16K;
+    public static final int FRONTEND_ATSC3_FEC_BCH_LDPC_64K =
+            Constants.FrontendAtsc3Fec.BCH_LDPC_64K;
+    public static final int FRONTEND_ATSC3_FEC_CRC_LDPC_16K =
+            Constants.FrontendAtsc3Fec.CRC_LDPC_16K;
+    public static final int FRONTEND_ATSC3_FEC_CRC_LDPC_64K =
+            Constants.FrontendAtsc3Fec.CRC_LDPC_64K;
+    public static final int FRONTEND_ATSC3_FEC_LDPC_16K = Constants.FrontendAtsc3Fec.LDPC_16K;
+    public static final int FRONTEND_ATSC3_FEC_LDPC_64K = Constants.FrontendAtsc3Fec.LDPC_64K;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_UNDEFINED,
+            FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET,
+            FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET})
+    public @interface FrontendAtsc3DemodOutputFormat {}
+
+    public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_UNDEFINED =
+            Constants.FrontendAtsc3DemodOutputFormat.UNDEFINED;
+    public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET =
+            Constants.FrontendAtsc3DemodOutputFormat.ATSC3_LINKLAYER_PACKET;
+    public static final int FRONTEND_ATSC3_DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET =
+            Constants.FrontendAtsc3DemodOutputFormat.BASEBAND_PACKET;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_DVBS_STANDARD_AUTO, FRONTEND_DVBS_STANDARD_S, FRONTEND_DVBS_STANDARD_S2,
+            FRONTEND_DVBS_STANDARD_S2X})
+    public @interface FrontendDvbsStandard {}
+
+    public static final int FRONTEND_DVBS_STANDARD_AUTO = Constants.FrontendDvbsStandard.AUTO;
+    public static final int FRONTEND_DVBS_STANDARD_S = Constants.FrontendDvbsStandard.S;
+    public static final int FRONTEND_DVBS_STANDARD_S2 = Constants.FrontendDvbsStandard.S2;
+    public static final int FRONTEND_DVBS_STANDARD_S2X = Constants.FrontendDvbsStandard.S2X;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_DVBC_ANNEX_UNDEFINED, FRONTEND_DVBC_ANNEX_A, FRONTEND_DVBC_ANNEX_B,
+            FRONTEND_DVBC_ANNEX_C})
+    public @interface FrontendDvbcAnnex {}
+
+    public static final int FRONTEND_DVBC_ANNEX_UNDEFINED = Constants.FrontendDvbcAnnex.UNDEFINED;
+    public static final int FRONTEND_DVBC_ANNEX_A = Constants.FrontendDvbcAnnex.A;
+    public static final int FRONTEND_DVBC_ANNEX_B = Constants.FrontendDvbcAnnex.B;
+    public static final int FRONTEND_DVBC_ANNEX_C = Constants.FrontendDvbcAnnex.C;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendDvbtTransmissionMode {}
+
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_UNDEFINED =
+            Constants.FrontendDvbtTransmissionMode.UNDEFINED;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_AUTO =
+            Constants.FrontendDvbtTransmissionMode.AUTO;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_2K =
+            Constants.FrontendDvbtTransmissionMode.MODE_2K;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_8K =
+            Constants.FrontendDvbtTransmissionMode.MODE_8K;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_4K =
+            Constants.FrontendDvbtTransmissionMode.MODE_4K;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_1K =
+            Constants.FrontendDvbtTransmissionMode.MODE_1K;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_16K =
+            Constants.FrontendDvbtTransmissionMode.MODE_16K;
+    public static final int FRONTEND_DVBT_TRANSMISSION_MODE_32K =
+            Constants.FrontendDvbtTransmissionMode.MODE_32K;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendDvbtBandwidth {}
+
+    public static final int FRONTEND_DVBT_BANDWIDTH_UNDEFINED =
+            Constants.FrontendDvbtBandwidth.UNDEFINED;
+    public static final int FRONTEND_DVBT_BANDWIDTH_AUTO = Constants.FrontendDvbtBandwidth.AUTO;
+    public static final int FRONTEND_DVBT_BANDWIDTH_8MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_8MHZ;
+    public static final int FRONTEND_DVBT_BANDWIDTH_7MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_7MHZ;
+    public static final int FRONTEND_DVBT_BANDWIDTH_6MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_6MHZ;
+    public static final int FRONTEND_DVBT_BANDWIDTH_5MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_5MHZ;
+    public static final int FRONTEND_DVBT_BANDWIDTH_1_7MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_1_7MHZ;
+    public static final int FRONTEND_DVBT_BANDWIDTH_10MHZ =
+            Constants.FrontendDvbtBandwidth.BANDWIDTH_10MHZ;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendDvbtConstellation {}
+
+    public static final int FRONTEND_DVBT_CONSTELLATION_UNDEFINED =
+            Constants.FrontendDvbtConstellation.UNDEFINED;
+    public static final int FRONTEND_DVBT_CONSTELLATION_AUTO =
+            Constants.FrontendDvbtConstellation.AUTO;
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_QPSK =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_QPSK;
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_16QAM =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_16QAM;
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_64QAM =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_64QAM;
+    public static final int FRONTEND_DVBT_CONSTELLATION_CONSTELLATION_256QAM =
+            Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendDvbtCoderate {}
+
+    public static final int FRONTEND_DVBT_CODERATE_UNDEFINED =
+            Constants.FrontendDvbtCoderate.UNDEFINED;
+    public static final int FRONTEND_DVBT_CODERATE_AUTO = Constants.FrontendDvbtCoderate.AUTO;
+    public static final int FRONTEND_DVBT_CODERATE_1_2 =
+            Constants.FrontendDvbtCoderate.CODERATE_1_2;
+    public static final int FRONTEND_DVBT_CODERATE_2_3 =
+            Constants.FrontendDvbtCoderate.CODERATE_2_3;
+    public static final int FRONTEND_DVBT_CODERATE_3_4 =
+            Constants.FrontendDvbtCoderate.CODERATE_3_4;
+    public static final int FRONTEND_DVBT_CODERATE_5_6 =
+            Constants.FrontendDvbtCoderate.CODERATE_5_6;
+    public static final int FRONTEND_DVBT_CODERATE_7_8 =
+            Constants.FrontendDvbtCoderate.CODERATE_7_8;
+    public static final int FRONTEND_DVBT_CODERATE_3_5 =
+            Constants.FrontendDvbtCoderate.CODERATE_3_5;
+    public static final int FRONTEND_DVBT_CODERATE_4_5 =
+            Constants.FrontendDvbtCoderate.CODERATE_4_5;
+    public static final int FRONTEND_DVBT_CODERATE_6_7 =
+            Constants.FrontendDvbtCoderate.CODERATE_6_7;
+    public static final int FRONTEND_DVBT_CODERATE_8_9 =
+            Constants.FrontendDvbtCoderate.CODERATE_8_9;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendDvbtGuardInterval {}
+
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_UNDEFINED =
+            Constants.FrontendDvbtGuardInterval.UNDEFINED;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_AUTO =
+            Constants.FrontendDvbtGuardInterval.AUTO;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_32 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_32;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_16 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_16;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_8 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_8;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_4 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_4;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_1_128 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_1_128;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_128 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_19_128;
+    public static final int FRONTEND_DVBT_GUARD_INTERVAL_INTERVAL_19_256 =
+            Constants.FrontendDvbtGuardInterval.INTERVAL_19_256;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @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})
+    public @interface FrontendIsdbsCoderate {}
+
+    public static final int FRONTEND_ISDBS_CODERATE_UNDEFINED =
+            Constants.FrontendIsdbsCoderate.UNDEFINED;
+    public static final int FRONTEND_ISDBS_CODERATE_AUTO = Constants.FrontendIsdbsCoderate.AUTO;
+    public static final int FRONTEND_ISDBS_CODERATE_1_2 =
+            Constants.FrontendIsdbsCoderate.CODERATE_1_2;
+    public static final int FRONTEND_ISDBS_CODERATE_2_3 =
+            Constants.FrontendIsdbsCoderate.CODERATE_2_3;
+    public static final int FRONTEND_ISDBS_CODERATE_3_4 =
+            Constants.FrontendIsdbsCoderate.CODERATE_3_4;
+    public static final int FRONTEND_ISDBS_CODERATE_5_6 =
+            Constants.FrontendIsdbsCoderate.CODERATE_5_6;
+    public static final int FRONTEND_ISDBS_CODERATE_7_8 =
+            Constants.FrontendIsdbsCoderate.CODERATE_7_8;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_ISDBT_MODE_UNDEFINED, FRONTEND_ISDBT_MODE_AUTO, FRONTEND_ISDBT_MODE_1,
+            FRONTEND_ISDBT_MODE_2, FRONTEND_ISDBT_MODE_3})
+    public @interface FrontendIsdbtMode {}
+
+    public static final int FRONTEND_ISDBT_MODE_UNDEFINED = Constants.FrontendIsdbtMode.UNDEFINED;
+    public static final int FRONTEND_ISDBT_MODE_AUTO = Constants.FrontendIsdbtMode.AUTO;
+    public static final int FRONTEND_ISDBT_MODE_1 = Constants.FrontendIsdbtMode.MODE_1;
+    public static final int FRONTEND_ISDBT_MODE_2 = Constants.FrontendIsdbtMode.MODE_2;
+    public static final int FRONTEND_ISDBT_MODE_3 = Constants.FrontendIsdbtMode.MODE_3;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FRONTEND_ISDBT_BANDWIDTH_UNDEFINED, FRONTEND_ISDBT_BANDWIDTH_AUTO,
+            FRONTEND_ISDBT_BANDWIDTH_8MHZ, FRONTEND_ISDBT_BANDWIDTH_7MHZ,
+            FRONTEND_ISDBT_BANDWIDTH_6MHZ})
+    public @interface FrontendIsdbtBandwidth {}
+
+    public static final int FRONTEND_ISDBT_BANDWIDTH_UNDEFINED =
+            Constants.FrontendIsdbtBandwidth.UNDEFINED;
+    public static final int FRONTEND_ISDBT_BANDWIDTH_AUTO = Constants.FrontendIsdbtBandwidth.AUTO;
+    public static final int FRONTEND_ISDBT_BANDWIDTH_8MHZ =
+            Constants.FrontendIsdbtBandwidth.BANDWIDTH_8MHZ;
+    public static final int FRONTEND_ISDBT_BANDWIDTH_7MHZ =
+            Constants.FrontendIsdbtBandwidth.BANDWIDTH_7MHZ;
+    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/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
new file mode 100644
index 0000000..548fa77
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner.filter;
+
+/**
+ * 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/media/java/android/media/tv/tuner/filter/FilterEvent.java b/media/java/android/media/tv/tuner/filter/FilterEvent.java
new file mode 100644
index 0000000..0a4fa86
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/FilterEvent.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * Demux filter event.
+ *
+ * @hide
+ */
+public abstract class FilterEvent {
+}
diff --git a/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
new file mode 100644
index 0000000..4da1d21
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/IpPayloadEvent.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+/**
+ * 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..5e45350
--- /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 {
+    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/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
new file mode 100644
index 0000000..dbd8c77
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * MMPT record event.
+ * @hide
+ */
+public class MmtpRecordEvent extends FilterEvent {
+    private int mScHevcIndexMask;
+    private long mByteNumber;
+}
diff --git a/media/java/android/media/tv/tuner/filter/PesEvent.java b/media/java/android/media/tv/tuner/filter/PesEvent.java
new file mode 100644
index 0000000..16536e2
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/PesEvent.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/**
+ * 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..5b5f8c9
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Section event.
+ * @hide
+ */
+public class SectionEvent {
+    // TODO: add constructor and getters
+    private int mTableId;
+    private int mVersion;
+    private int mSectionNum;
+    private int mDataLength;
+}
diff --git a/media/java/android/media/tv/tuner/filter/TemiEvent.java b/media/java/android/media/tv/tuner/filter/TemiEvent.java
new file mode 100644
index 0000000..3841604
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/TemiEvent.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/**
+ * TEMI event.
+ * @hide
+ */
+public class TemiEvent extends FilterEvent {
+    private long mPts;
+    private byte mDescrTag;
+    private byte[] mDescrData;
+}
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
new file mode 100644
index 0000000..875b5bd
--- /dev/null
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+/**
+ * TS record event.
+ * @hide
+ */
+public class TsRecordEvent extends FilterEvent {
+    private int mTpid;
+    private int mIndexMask;
+    private long mByteNumber;
+}
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 da52696..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[] = {
@@ -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/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/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..27ad4fc 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
@@ -115,7 +115,6 @@
             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 +126,6 @@
             android:id="@+id/assist"
             style="@style/NavigationBarButton"
             systemui:icon="@drawable/ic_mic_white"
-            systemui:useMoreIcon="false"
         />
 
     </LinearLayout>
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..bafbb64 100644
--- a/packages/CarSystemUI/res/layout/car_navigation_button.xml
+++ b/packages/CarSystemUI/res/layout/car_navigation_button.xml
@@ -18,12 +18,34 @@
 -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <com.android.keyguard.AlphaOptimizedImageButton
+    <LinearLayout
+        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:gravity="center"
         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: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/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/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..d08fbbc 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,66 @@
  * 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 {
 
+    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 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;
 
     private Context mContext;
+    private AlphaOptimizedImageButton mIcon;
+    private AlphaOptimizedImageButton mMoreIcon;
     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 +115,173 @@
         return mHasUnseen;
     }
 
+    /**
+     * @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);
+        mIcon.setClickable(false);
+        // 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.setClickable(false);
+        mMoreIcon.setAlpha(mSelectedAlpha);
+        mMoreIcon.setVisibility(GONE);
+    }
+
     private void updateImage() {
         if (mHasUnseen) {
-            setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
+            mIcon.setImageResource(mSelected ? UNSEEN_SELECTED_ICON_RESOURCE_ID
                     : UNSEEN_ICON_RESOURCE_ID);
         } else {
-            setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
+            mIcon.setImageResource(mSelected ? mSelectedIconResourceId : mIconResourceId);
         }
     }
 }
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..e1ee616
--- /dev/null
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/navigationbar/car/CarNavigationButtonTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.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());
+    }
+
+    private String getCurrentActivityName() {
+        return mActivityManager.getRunningTasks(1).get(0).topActivity.flattenToShortString();
+    }
+}
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 3f99d10..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>
@@ -267,9 +267,9 @@
     <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>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index f9ac3a5..d0307ec 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -276,7 +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>
-    <string name="bluetooth_enable_gabeldorsche_summary" msgid="8472344901097607030">"Bluetooth Gabeldorsche सुविधा का स्टैक चालू करें."</string>
+    <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/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/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/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/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/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/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/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
new file mode 100644
index 0000000..155a6d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/DisplayId.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+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..c1934c6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -86,7 +86,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;
@@ -2102,7 +2102,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/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/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/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/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 c74286d..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.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();
-    }
-
-    @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 cd56d06..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.isClassifierEnabled()) {
-            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/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index 88edf8e..625d884 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -24,7 +24,7 @@
 import android.widget.FrameLayout;
 
 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;
@@ -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);
         });
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/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/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/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/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/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/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/ip/RouterAdvertisementDaemon.java b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 4147413..4396cdb 100644
--- a/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/packages/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -30,6 +30,7 @@
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
 import android.net.util.InterfaceParams;
+import android.net.util.TetheringUtils;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.StructTimeval;
@@ -613,7 +614,7 @@
                     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);
+            TetheringUtils.setupRaSocket(mSocket, mInterface.index);
         } catch (ErrnoException | IOException e) {
             Log.e(TAG, "Failed to create RA daemon socket: " + e);
             return false;
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..2fb73ce
--- /dev/null
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.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 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;
+}
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..85164ed 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -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..0b5759b 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -633,8 +633,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);
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..1d59986 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -84,6 +84,7 @@
      */
     @VisibleForTesting
     public Tethering makeTethering(TetheringDependencies deps) {
+        System.loadLibrary("tetherutilsjni");
         return new Tethering(deps);
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 3b51329..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;
@@ -2637,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;
             }
@@ -2680,10 +2677,22 @@
     /**
      * Returns whether we made a request to show inline suggestions.
      */
-    private boolean requestShowInlineSuggestions(List<Slice> inlineSuggestionSlices,
-            FillResponse response) {
-        final IInlineSuggestionsResponseCallback inlineContentCallback =
-                getInlineSuggestionsResponseCallback();
+    private boolean requestShowInlineSuggestions(FillResponse response) {
+        IInlineSuggestionsResponseCallback inlineContentCallback = null;
+        synchronized (mLock) {
+            if (mInlineSuggestionsResponseCallbackFuture != null) {
+                try {
+                    inlineContentCallback = mInlineSuggestionsResponseCallbackFuture.get(
+                            INLINE_REQUEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+                } catch (TimeoutException e) {
+                    Log.w(TAG, "Exception getting inline suggestions callback in time: " + e);
+                } catch (CancellationException e) {
+                    Log.w(TAG, "Inline suggestions callback cancelled");
+                } catch (InterruptedException | ExecutionException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        }
 
         if (inlineContentCallback == null) {
             Log.w(TAG, "Session input method callback is not set yet");
@@ -2696,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() {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 5b98f06..8d22300 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",
     ],
 
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/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 18ccd01..3d455ee 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1119,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) {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 783715c..25bad64 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;
@@ -49,6 +50,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;
@@ -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;
@@ -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;
@@ -797,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);
                         }
@@ -857,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);
                         }
@@ -1290,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)) {
@@ -2051,6 +2068,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, "  ");
 
@@ -2142,7 +2193,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);
@@ -2168,13 +2219,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
@@ -2483,11 +2553,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);
             }
@@ -2495,14 +2568,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);
             }
@@ -2651,8 +2726,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/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/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/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dd1f370..4bc0297 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,7 +319,6 @@
 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;
@@ -2176,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()));
@@ -4338,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();
@@ -4361,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);
@@ -12594,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.
@@ -12627,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,
@@ -12715,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();
                 }
@@ -12727,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,
@@ -12867,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();
@@ -13174,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.
@@ -13209,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);
 
@@ -13313,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];
@@ -13447,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();
@@ -15098,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)) {
@@ -16166,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);
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/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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 5bbb517..a7593c7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -17,6 +17,11 @@
 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.OP_CAMERA;
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
@@ -53,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;
@@ -632,6 +636,7 @@
     }
 
     private final class FeatureOp {
+        public final @Nullable String featureId;
         public final @NonNull Op parent;
 
         /**
@@ -658,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;
         }
 
@@ -676,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);
         }
 
         /**
@@ -720,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);
         }
 
         /**
@@ -780,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);
         }
 
         /**
@@ -820,8 +832,8 @@
                 mAccessEvents.put(makeKey(event.getUidState(), OP_FLAG_SELF), finishedEvent);
 
                 mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
-                        parent.packageName, event.getUidState(), AppOpsManager.OP_FLAG_SELF,
-                        finishedEvent.getDuration());
+                        parent.packageName, featureId, event.getUidState(),
+                        AppOpsManager.OP_FLAG_SELF, finishedEvent.getDuration());
 
                 mInProgressStartOpEventPool.release(event);
 
@@ -1031,7 +1043,7 @@
 
             featureOp = mFeatures.get(featureId);
             if (featureOp == null) {
-                featureOp = new FeatureOp(parent);
+                featureOp = new FeatureOp(featureId, parent);
                 mFeatures.put(featureId, featureOp);
             }
 
@@ -1697,18 +1709,47 @@
         }
     }
 
+    /**
+     * 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();
+    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,
@@ -1718,22 +1759,16 @@
                 ? 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();
+    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,
@@ -1743,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
@@ -2631,8 +2666,6 @@
                             + 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;
                 }
@@ -2645,8 +2678,6 @@
                             + 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;
                 }
@@ -2654,9 +2685,6 @@
             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);
 
@@ -2938,8 +2966,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 uidMode;
                 }
             } else {
@@ -2952,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;
                 }
             }
@@ -4437,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 + "  ");
@@ -4563,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++) {
@@ -4583,6 +4617,7 @@
                         return;
                     }
                     dumpOp = Shell.strOpToOp(args[i], pw);
+                    dumpFilter |= FILTER_BY_OP_NAMES;
                     if (dumpOp < 0) {
                         return;
                     }
@@ -4593,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,
@@ -4604,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) {
@@ -4640,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 #");
@@ -4944,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, "        ");
                     }
                 }
             }
@@ -5043,7 +5089,8 @@
 
         // Must not hold the appops lock
         if (dumpHistory && !dumpWatchers) {
-            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpOp);
+            mHistoricalRegistry.dump("  ", pw, dumpUid, dumpPackage, dumpFeatureId, dumpOp,
+                    dumpFilter);
         }
     }
 
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/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/integrity/IntegrityFileManager.java b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
index 30cafaa..f90fab4 100644
--- a/services/core/java/com/android/server/integrity/IntegrityFileManager.java
+++ b/services/core/java/com/android/server/integrity/IntegrityFileManager.java
@@ -25,6 +25,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.integrity.model.RuleMetadata;
 import com.android.server.integrity.parser.RuleBinaryParser;
+import com.android.server.integrity.parser.RuleIndexingController;
 import com.android.server.integrity.parser.RuleMetadataParser;
 import com.android.server.integrity.parser.RuleParseException;
 import com.android.server.integrity.parser.RuleParser;
@@ -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;
@@ -64,7 +62,10 @@
     // 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() {
@@ -103,6 +104,8 @@
                 Slog.e(TAG, "Error reading metadata file.", e);
             }
         }
+
+        updateRuleIndexingController();
     }
 
     /**
@@ -112,7 +115,8 @@
      */
     public boolean initialized() {
         return new File(mRulesDir, RULES_FILE).exists()
-                && new File(mRulesDir, METADATA_FILE).exists();
+                && new File(mRulesDir, METADATA_FILE).exists()
+                && new File(mRulesDir, INDEXING_FILE).exists();
     }
 
     /** Write rules to persistent storage. */
@@ -125,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();
     }
 
     /**
@@ -140,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;
             }
@@ -168,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
index c8d318f..62815a9 100644
--- a/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
+++ b/services/core/java/com/android/server/integrity/serializer/ByteTrackedOutputStream.java
@@ -27,7 +27,7 @@
  */
 public class ByteTrackedOutputStream {
 
-    private static long sWrittenBytesCount;
+    private static int sWrittenBytesCount;
     private static OutputStream sOutputStream;
 
     public ByteTrackedOutputStream(OutputStream outputStream) {
@@ -47,7 +47,7 @@
     /**
      * Returns the total number of bytes written into the output stream at the requested time.
      */
-    public long getWrittenBytesCount() {
+    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 22af085..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;
@@ -52,20 +55,14 @@
 /** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
 public class RuleBinarySerializer implements RuleSerializer {
 
-    // The parsing time seems acceptable for 100 rules based on the tests in go/ic-rule-file-format.
-    private static final int INDEXING_BLOCK_SIZE = 100;
-
-    private static final String START_INDEXING_KEY = "START_KEY";
-    private static final String END_INDEXING_KEY = "END_KEY";
-
     // Get the byte representation for a list of rules.
     @Override
     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);
         }
@@ -74,27 +71,38 @@
     // 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 originalOutputStream)
+            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, TreeMap<String, List<Rule>>> indexedRules =
                     RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules);
 
-            ByteTrackedOutputStream outputStream =
-                    new ByteTrackedOutputStream(originalOutputStream);
+            ByteTrackedOutputStream ruleFileByteTrackedOutputStream =
+                    new ByteTrackedOutputStream(rulesFileOutputStream);
 
-            serializeRuleFileMetadata(formatVersion, outputStream);
+            serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream);
 
-            Map<String, Long> packageNameIndexes =
-                    serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED), outputStream);
-            Map<String, Long> appCertificateIndexes =
-                    serializeRuleList(indexedRules.get(APP_CERTIFICATE_INDEXED), outputStream);
-            Map<String, Long> unindexedRulesIndex =
-                    serializeRuleList(indexedRules.get(NOT_INDEXED), outputStream);
+            Map<String, Integer> packageNameIndexes =
+                    serializeRuleList(indexedRules.get(PACKAGE_NAME_INDEXED),
+                            ruleFileByteTrackedOutputStream);
+            indexingFileOutputStream.write(
+                    serializeIndexes(packageNameIndexes, /* isIndexed= */true));
 
-            // TODO(b/145493956): Write these indexes into a index file provided by integrity file
-            //  manager.
+            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);
         }
@@ -109,15 +117,15 @@
         outputStream.write(bitOutputStream.toByteArray());
     }
 
-    private Map<String, Long> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
+    private Map<String, Integer> serializeRuleList(TreeMap<String, List<Rule>> rulesMap,
             ByteTrackedOutputStream outputStream)
             throws IOException {
         Preconditions.checkArgument(rulesMap != null,
                 "serializeRuleList should never be called with null rule list.");
 
         BitOutputStream bitOutputStream = new BitOutputStream();
-        Map<String, Long> indexMapping = new TreeMap();
-        long indexTracker = 0;
+        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()) {
@@ -210,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/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 4c04dbc..4194432 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -56,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);
         }
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/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/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 82d2250..22ba1bf 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -233,6 +233,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");
@@ -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)) {
@@ -835,25 +860,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 +1007,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 +1023,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 +1039,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 +1070,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) {
@@ -1103,7 +1170,7 @@
             // 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())
@@ -1120,6 +1187,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 {
@@ -1149,6 +1233,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/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 2390da5e..4a45730 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1780,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);
             }
         }
     }
@@ -1819,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)) {
@@ -3046,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);
@@ -3059,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;
             }
         }
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..c868953 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -140,6 +140,7 @@
 import java.util.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 +512,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 +1144,7 @@
      * permissions.
      */
     private boolean markAsCommitted(@NonNull IntentSender statusReceiver) {
-        Preconditions.checkNotNull(statusReceiver);
+        Objects.requireNonNull(statusReceiver);
 
         List<PackageInstallerSession> childSessions = getChildSessions();
 
@@ -1408,8 +1409,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 +1608,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;
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/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/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..1455d4a 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(
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 9cd6f16..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
@@ -3028,7 +3028,7 @@
         mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
                 "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
                         + " to register permissions as one time.");
-        packageName = Preconditions.checkNotNull(packageName);
+        Objects.requireNonNull(packageName);
 
         long token = Binder.clearCallingIdentity();
         try {
@@ -3044,7 +3044,7 @@
         mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
                 "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
                         + " to remove permissions as one time.");
-        Preconditions.checkNotNull(packageName);
+        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 858180b..5a124a7 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -697,16 +697,7 @@
                     accessibilityShortcutActivated();
                     break;
                 case MSG_BUGREPORT_TV:
-                    boolean customBugreport = mContext.getResources().getBoolean(
-                            com.android.internal.R.bool.config_customBugreport);
-                    if (customBugreport) {
-                        Log.i(TAG, "Triggering a custom bugreport!");
-                        Intent intent = new Intent(ACTION_CUSTOM_BUGREPORT_REQUESTED);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-                        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
-                    } else {
-                        requestFullBugreport();
-                    }
+                    requestFullBugreport();
                     break;
                 case MSG_ACCESSIBILITY_TV:
                     if (mAccessibilityShortcutController.isAccessibilityShortcutAvailable(false)) {
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/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index ae3f368..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,7 @@
         return enableRollbackForPackageSession(newRollback.rollback, packageSession);
     }
 
+    @WorkerThread
     private void removeRollbackForPackageSessionId(int sessionId) {
         if (LOCAL_LOGV) {
             Slog.v(TAG, "removeRollbackForPackageSessionId=" + sessionId);
@@ -814,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.
@@ -888,6 +913,7 @@
         });
     }
 
+    @WorkerThread
     private void snapshotUserDataInternal(String packageName, int[] userIds) {
         if (LOCAL_LOGV) {
             Slog.v(TAG, "snapshotUserData pkg=" + packageName
@@ -907,6 +933,7 @@
         }
     }
 
+    @WorkerThread
     private void restoreUserDataInternal(
             String packageName, int[] userIds, int appId, String seInfo) {
         if (LOCAL_LOGV) {
@@ -1021,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;
@@ -1043,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)
@@ -1051,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;
@@ -1067,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 {
@@ -1083,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();
@@ -1097,6 +1129,7 @@
         }
     }
 
+    @WorkerThread
     private class SessionCallback extends PackageInstaller.SessionCallback {
 
         @Override
@@ -1149,6 +1182,7 @@
      * @return the Rollback instance for a successfully enable-completed rollback,
      * or null on error.
      */
+    @WorkerThread
     private Rollback completeEnableRollback(NewRollback newRollback) {
         Rollback rollback = newRollback.rollback;
         if (LOCAL_LOGV) {
@@ -1185,6 +1219,7 @@
         return rollback;
     }
 
+    @WorkerThread
     @GuardedBy("rollback.getLock")
     private void makeRollbackAvailable(Rollback rollback) {
         if (LOCAL_LOGV) {
@@ -1205,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) {
@@ -1218,6 +1254,7 @@
         return null;
     }
 
+    @WorkerThread
     @GuardedBy("mLock")
     private int allocateRollbackIdLocked() {
         int n = 0;
@@ -1247,6 +1284,7 @@
         }
     }
 
+    @AnyThread
     private void enforceManageRollbacks(@NonNull String message) {
         if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
                         Manifest.permission.MANAGE_ROLLBACKS))
@@ -1370,6 +1408,7 @@
         }
     }
 
+    @WorkerThread
     @GuardedBy("mLock")
     private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
         int rollbackId = allocateRollbackIdLocked();
@@ -1411,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/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/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/utils/quota/UptcMap.java b/services/core/java/com/android/server/utils/quota/UptcMap.java
index 7b49913..80f4f0a 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,6 +96,23 @@
     }
 
     /**
+     * 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.
+     */
+    @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 index for which {@link #getUserIdAtIndex(int)} would return the specified userId,
      * or a negative number if the specified userId is not mapped.
      */
@@ -160,4 +178,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 8bedeae..65d0c4c 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -555,7 +555,7 @@
 
     private RemoteAnimationDefinition mRemoteAnimationDefinition;
 
-    private AnimatingActivityRegistry mAnimatingActivityRegistry;
+    AnimatingActivityRegistry mAnimatingActivityRegistry;
 
     private Task mLastParent;
 
@@ -3088,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));
@@ -3100,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);
         }
 
@@ -3436,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());
     }
 
     /**
@@ -4861,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);
@@ -5344,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));
                 }
             }
 
@@ -5951,7 +5950,7 @@
 
     @Override
     void prepareSurfaces() {
-        final boolean show = isVisible() || isAnimating();
+        final boolean show = isVisible() || isAnimating(PARENTS);
 
         if (mSurfaceControl != null) {
             if (show && !mLastSurfaceShowing) {
@@ -5979,7 +5978,7 @@
     }
 
     void attachThumbnailAnimation() {
-        if (!isAnimating()) {
+        if (!isAnimating(PARENTS)) {
             return;
         }
         final GraphicBuffer thumbnailHeader =
@@ -5999,7 +5998,7 @@
      * {@link android.app.ActivityOptions#ANIM_OPEN_CROSS_PROFILE_APPS} animation.
      */
     void attachCrossProfileAppsThumbnailAnimation() {
-        if (!isAnimating()) {
+        if (!isAnimating(PARENTS)) {
             return;
         }
         clearThumbnail();
@@ -6094,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)) {
+        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.
-            mStackSupervisor.processStoppingActivities(null /* launchedActivity */,
-                    true /* onlyUpdateVisibility */, true /* unused */, null /* unused */);
         }
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
@@ -7511,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 ef34327..c959439 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -784,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()) {
@@ -1414,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);
@@ -4889,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 ed73943..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;
@@ -2068,23 +2069,6 @@
     }
 
     /**
-     * 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.
-     */
-    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;
-            }
-        }
-
-        return false;
-    }
-
-    /**
      * Processes the activities to be stopped or destroyed. This should be called when the resumed
      * activities are idle or drawn.
      */
@@ -2403,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);
     }
@@ -2701,6 +2691,10 @@
                         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) {
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 6d3fabc..7f94445 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3342,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);
                 }
 
@@ -4784,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;
                     }
 
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/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/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 a5c90a1..e310fc1 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -91,6 +91,9 @@
     }
 
     private boolean processActivity(ActivityRecord r, boolean isTargetTask) {
+        // End processing if we have reached the root.
+        if (r == mRoot) return true;
+
         mAllActivities.add(r);
         final int flags = r.info.flags;
         final boolean finishOnTaskLaunch =
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/Task.java b/services/core/java/com/android/server/wm/Task.java
index 914b95c..9a140da 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -94,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;
 
@@ -2555,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
@@ -2568,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);
     }
@@ -2594,7 +2617,7 @@
     @Override
     RemoteAnimationTarget createRemoteAnimationTarget(
             RemoteAnimationController.RemoteAnimationRecord record) {
-        final ActivityRecord activity = getTopVisibleActivity();
+        final ActivityRecord activity = getTopMostActivity();
         return activity != null ? activity.createRemoteAnimationTarget(record) : null;
     }
 
@@ -2721,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();
         }
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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5daf567..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;
     }
 
     /**
@@ -1895,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(
@@ -2040,7 +2040,8 @@
     }
 
     boolean okToDisplay() {
-        return mDisplayContent != null && mDisplayContent.okToDisplay();
+        final DisplayContent dc = getDisplayContent();
+        return dc != null && dc.okToDisplay();
     }
 
     boolean okToAnimate() {
@@ -2048,7 +2049,8 @@
     }
 
     boolean okToAnimate(boolean ignoreFrozen) {
-        return mDisplayContent != null && mDisplayContent.okToAnimate(ignoreFrozen);
+        final DisplayContent dc = getDisplayContent();
+        return dc != null && dc.okToAnimate(ignoreFrozen);
     }
 
     @Override
@@ -2090,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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 2ac1f39..4e768da 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -210,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;
@@ -650,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,
@@ -779,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()
@@ -971,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);
@@ -1003,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) {
@@ -1573,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();
     }
 
     /**
@@ -1799,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;
@@ -2068,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));
 
@@ -4287,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 1a11766..486616d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1398,7 +1398,7 @@
             mWin.getDisplayContent().adjustForImeIfNeeded();
         }
 
-        return mWin.isAnimating(TRANSITION | PARENTS);
+        return mWin.isAnimating(PARENTS);
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c275ccc..398ce50 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5807,8 +5807,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(
@@ -7725,7 +7726,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)
@@ -8005,7 +8012,7 @@
         }
     }
 
-    private boolean canProfileOwnerAccessDeviceIds(int userId) {
+    private boolean isProfileOwnerOfOrganizationOwnedDevice(int userId) {
         synchronized (getLockObject()) {
             return mOwners.isProfileOwnerOfOrganizationOwnedDevice(userId);
         }
@@ -8028,7 +8035,7 @@
     }
 
     private boolean isProfileOwnerOfOrganizationOwnedDevice(ComponentName who, int userId) {
-        return isProfileOwner(who, userId) && canProfileOwnerAccessDeviceIds(userId);
+        return isProfileOwner(who, userId) && isProfileOwnerOfOrganizationOwnedDevice(userId);
     }
 
     @Override
@@ -8524,7 +8531,6 @@
         if (!mHasFeature) {
             return null;
         }
-
         synchronized (getLockObject()) {
             return mOwners.getProfileOwnerComponent(userHandle);
         }
@@ -8555,7 +8561,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);
                     }
@@ -8606,7 +8612,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
@@ -8819,6 +8825,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);
@@ -14496,6 +14522,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;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cfe1318..08221f9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -214,6 +214,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 =
@@ -1975,6 +1977,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/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 0504d14..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;
@@ -287,7 +289,7 @@
         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);
@@ -300,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);
@@ -312,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);
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 015e574f2..1232340 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -43,6 +43,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/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 133d35d..ee94dd6 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;
@@ -5570,6 +5571,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/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/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/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/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/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 06b3dc1..a7275fc7 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))
+        assertThat(mUserManager.getUserTypeForUser(asHandle(userId)))
                 .isEqualTo(userTypeDetails.getName());
 
         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/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/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/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/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 5028585..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());
     }
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/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/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/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..aff1391 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
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 599cd9f..8139330c 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;
@@ -1886,7 +1886,7 @@
 
         synchronized (mNetworkRegistrationInfos) {
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
-                if (networkRegistrationInfo.getDomain() == domain) {
+                if ((networkRegistrationInfo.getDomain() & domain) != 0) {
                     list.add(new NetworkRegistrationInfo(networkRegistrationInfo));
                 }
             }
@@ -1902,7 +1902,6 @@
      * @param transportType The transport type
      * @return The matching {@link NetworkRegistrationInfo}
      * @hide
-     *
      */
     @Nullable
     @SystemApi
@@ -1911,7 +1910,7 @@
         synchronized (mNetworkRegistrationInfos) {
             for (NetworkRegistrationInfo networkRegistrationInfo : mNetworkRegistrationInfos) {
                 if (networkRegistrationInfo.getTransportType() == transportType
-                        && networkRegistrationInfo.getDomain() == domain) {
+                        && (networkRegistrationInfo.getDomain() & domain) != 0) {
                     return new NetworkRegistrationInfo(networkRegistrationInfo);
                 }
             }
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..4c8a81b 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;
@@ -1323,6 +1323,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 +2181,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 +3302,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 20c9f6c..df8c26d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -36,13 +36,13 @@
 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;
@@ -1543,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
@@ -7731,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()));
@@ -10478,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);
@@ -10558,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();
@@ -11958,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.
@@ -12082,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..998b39d 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;
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 9d721fc..b846a10 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2118,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/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/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/wifi/Android.bp b/wifi/Android.bp
index 35a892a..3934af1 100644
--- a/wifi/Android.bp
+++ b/wifi/Android.bp
@@ -57,6 +57,7 @@
     libs: [
         // TODO(b/140299412) should be framework-system-stubs once we fix all @hide dependencies
         "framework-minus-apex",
+        "unsupportedappusage",
     ],
     srcs: [
         ":framework-wifi-updatable-sources",
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/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/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 2a165d3..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];
@@ -2590,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..148e8a4 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;
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/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/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());